目前不清退的交易所推荐:
1、全球第二大交易所OKX欧意
国区邀请链接: https://www.dqvzgdmisn.com/zh-hans/join/1837888 币种多,交易量大!
国际邀请链接:https://www.okx.com/join/1837888 注册简单,交易不需要实名,新用户能开合约,币种多,交易量大!
2、老牌交易所比特儿现改名叫芝麻开门 :https://www.gate.ac/signup/XgRDAQ8?ref_type=103
全球最大交易所币安,国区邀请链接:https://accounts.binance.com/zh-CN/register?ref=16003031 币安注册不了IP地址用香港,居住地选香港,认证照旧,邮箱推荐如gmail、outlook。支持币种多,交易安全!
买好币上KuCoin:https://www.kucoin.com/r/af/1f7w3 CoinMarketCap前五的交易所,注册友好操简单快捷!
本文档的目的是提供使用 TON Blockchain Lite 客户端和相关软件在 TON 区块链测试网络中编译和创建简单智能合约(简单钱包)的分步说明。
我们在这里假设 Lite Client 已经正确下载、编译和安装。
请注意,本教程适用于测试网,因此您必须使用配置https://newton-blockchain.github.io/testnet-global.config.json
1. 智能合约地址
TON 网络中的智能合约地址由两部分组成:
(a) 工作链 ID(带符号的 32 位整数)和
(b) 工作链内的地址(64-512 位,具体取决于工作链)。
目前,只有主链(workchain_id=-1)和偶尔的基本工作链(workchain_id=0)在 TON 区块链网络中运行。它们都有 256 位地址,因此我们以后假设 workchain_id 为 0 或 -1,并且工作链内的地址正好是 256 位。
在上述条件下,智能合约地址可以表示为以下形式:
A)“原始”:<十进制工作链ID>:<64位十六进制数字与地址>
B)“用户友好”,这是通过首先生成:
- 一个标签字节(0x11 表示“可反弹”地址,0x51 表示“不可反弹”;如果地址不应被生产网络中运行的软件接受,则添加 +0x80)
- 一个字节,包含一个带符号的 8 位整数和 workchain_id(基本工作链为 0x00,主链为 0xff)
- 32 个字节,包含工作链内 256 位的智能合约地址(大端)
- 包含前 34 个字节的 CRC16-CCITT 的 2 个字节
在情况 B) 中,如此获得的 36 个字节然后使用 base64(即,使用数字、大小写拉丁字母、’/’ 和 ‘+’)或 base64url(使用 ‘_’ 和 ‘-‘ 而不是 ‘ /’ 和 ‘+’),产生 48 个可打印的非空格字符。
例子:
“测试提供者”(位于测试网络主链中的特殊智能合约,向任何提出请求的人提供多达 20 个测试币)具有地址
-1:fcb91a3a3816d0f7b8c2c76108b8a9bc5a6b7a55bd79f8ab101c52db29232260
以“原始”形式(注意大写拉丁字母 ‘A’..’F’ 可以用来代替 ‘a’..’f’)
和
kf/8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny
(base64)
kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny
(base64url)
在“用户友好”形式中(由用户友好的客户端显示)。请注意,两种形式(base64 和 base64url)都是有效的并且必须被接受。
顺便说一句,与 TON 区块链相关的其他二进制数据具有类似的“装甲”base64 表示,不同之处在于它们的第一个字节。例如,无处不在的 256 位 Ed25519 公钥通过首先创建一个 36 字节序列来表示,如下所示:
- 一个标记字节 0x3E,表示这是一个公钥
- 一个标签字节 0xE6,表示这是 Ed25519 公钥
- 32 个字节,包含 Ed25519 公钥的标准二进制表示
- 2 个字节,包含前 34 个字节的 CRC16-CCITT 的大端表示。
生成的 36 字节序列以标准方式转换为 48 个字符的 base64 或 base64url 字符串。例如,Ed25519 公钥E39ECDA0A7B0C60A7107EC43967829DBE8BC356A49B9DFC6186B3EAC74B5477D
(通常由 32 个字节的序列表示0xE3, 0x9E, ..., 0x7D
)具有以下“装甲”表示:
Pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2
检查智能合约的状态
借助 TON Lite 客户端检查智能合约的状态很容易。对于上述示例智能合约,您将运行 Lite 客户端并输入以下命令:
> last
...
> getaccount -1:fcb91a3a3816d0f7b8c2c76108b8a9bc5a6b7a55bd79f8ab101c52db29232260
or
> getaccount kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny
你会看到这样的东西:
请注意,代码、注释和/或文档可能包含参数、方法和定义“gram”、“nanogram”等。这是由 Telegram 开发的原始 TON 代码的遗产。Gram 加密货币从未发行过。TON 的货币是 TON Coin,TON 测试网的货币是 Test TON Coin。
got account state for -1 : FCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260 with respect to blocks (-1,8000000000000000,2075):BFE876CE2085274FEDAF1BD80F3ACE50F42B5A027DF230AD66DCED1F09FB39A7:522C027A721FABCB32574E3A809ABFBEE6A71DE929C1FA2B1CD0DDECF3056505
account state is (account
addr:(addr_std
anycast:nothing workchain_id:-1 address:xFCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260)
storage_stat:(storage_info
used:(storage_used
cells:(var_uint len:1 value:3)
bits:(var_uint len:2 value:707)
public_cells:(var_uint len:0 value:0)) last_paid:1568899526
due_payment:nothing)
storage:(account_storage last_trans_lt:2310000003
balance:(currencies
grams:(nanograms
amount:(var_uint len:6 value:9998859500889))
other:(extra_currencies
dict:hme_empty))
state:(account_active
(
split_depth:nothing
special:nothing
code:(just
value:(raw@^Cell
x{}
x{FF0020DD2082014C97BA9730ED44D0D70B1FE0A4F260D31F01ED44D0D31FD166BAF2A1F8000120D74A8E11D307D459821804A817C80073FB0201FB00DED1A4C8CB1FC9ED54}
))
data:(just
value:(raw@^Cell
x{}
x{00009A15}
))
library:hme_empty))))
x{CFFFCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB2923226020680B0C2EC1C0E300000000226BF360D8246029DFF56534_}
x{FF0020DD2082014C97BA9730ED44D0D70B1FE0A4F260D31F01ED44D0D31FD166BAF2A1F8000120D74A8E11D307D459821804A817C80073FB0201FB00DED1A4C8CB1FC9ED54}
x{00000003}
last transaction lt = 2310000001 hash = 73F89C6F8910F598AD84504A777E5945C798AC8C847FF861C090109665EAC6BA
第一个信息行got account state ... for ...
显示账户地址和主链区块标识符,账户状态已被转储。请注意,即使帐户状态在后续块中发生更改,该getaccount xxx
命令也会返回相同的结果,直到参考块被last
命令更新为更新的值。通过这种方式可以研究所有帐户的状态并获得一致的结果。
该account state is (account ...
行开始漂亮打印的帐户状态的反序列化视图。它是 TL-B 数据类型 Account 的反序列化,用于表示 TON 区块链中的账户状态,如 TON 区块链文档中所述。(您可以在源文件中找到用于反序列化的 TL-B 方案crypto/block/block.tlb
;请注意,如果该方案已过期,反序列化可能会崩溃。)
最后,以(“原始转储”)开头的最后几行x{CFF...
包含显示为单元格树的相同信息。在这种情况下,我们有一个包含数据位CFF...34_
的根单元格(下划线表示要删除最后一个二进制 1 和所有后续二进制零,因此十六进制4_
对应于 binary 0
),以及两个作为其子单元格的单元格(显示为 1 -空格缩进)。
我们可以看到这x{FF0020DD20...}
是这个智能合约的代码。如果我们查阅 TON 虚拟机文档的附录 A,我们甚至可以反汇编这段代码:FF00
is SETCP 0
、20
is DUP
、DD
is IFNOTRET
、20
is DUP
、等等。(顺便提一下,你可以在源文件中找到这个智能合约的源代码crypto/block/new-testgiver.fif
)
我们还可以看到x{00009A15}
(你看到的实际值可能不同)是这个智能合约的持久化数据。它实际上是一个无符号的 32 位整数,被智能合约用作迄今为止执行的操作的计数器。请注意,这个值是大端的(即 3 被编码为x{00000003}
,而不是x{03000000}
),就像 TON 区块链中的所有整数一样。在这种情况下,计数器等于0x9A15
= 39445
。
智能合约的当前余额很容易在输出的漂亮打印部分中看到。在这种情况下,我们看到... balance:(currencies:(grams:(nanograms:(... value:1000000000000000...))))
,它是(测试)纳吨的帐户余额(在本例中为一百万测试 TON 硬币;您看到的实际数字可能更小)。如果您研究 中提供的 TL-B 方案crypto/block/scheme.tlb
,您将能够在原始转储部分中以二进制大端形式找到这个数字 (10^15)(它位于数据位的末尾附近)根单元格)。
3. 编译新的智能合约
在将新的智能合约上传到 TON 区块链之前,您需要确定其代码和数据,并将它们以序列化的形式保存到文件中(称为“bag-of-cells”或 BOC 文件,通常带有 .boc 后缀)。让我们考虑一个简单的钱包智能合约的情况,它在其持久数据中存储了一个 32 位操作计数器和一个其所有者的 256 位 Ed25519 公钥。
显然,您需要一些工具来开发智能合约——即 TON 智能合约编译器。基本上,TON 智能合约编译器是一个程序,它以专门的高级编程语言读取智能合约的源代码,并从该源代码创建一个 .boc 文件。
其中一个这样的工具是 Fift 解释器,它包含在这个发行版中,可以帮助创建简单的智能合约。更大的智能合约应该使用更复杂的工具来开发(例如这个发行版中包含的 FunC 编译器,它从 FunC 源文件创建 Fift 汇编文件;你可以在目录中找到一些 FunC 智能合约源crypto/smartcont
)。但是,Fift 足以用于演示目的。
考虑包含简单钱包智能合约源的文件new-wallet.fif
(通常位于crypto/smartcont/new-wallet.fif
源目录):
#!/usr/bin/env fift -s
"TonUtil.fif" include
"Asm.fif" include
{ ."usage: " @' $0 type ." <workchain-id> [<filename-base>]" cr
."Creates a new wallet in specified workchain, with private key saved to or loaded from <filename-base>.pk" cr
."('new-wallet.pk' by default)" cr 1 halt
} : usage
$# 1- -2 and ' usage if
$1 parse-workchain-id =: wc // set workchain id from command line argument
def? $2 { @' $2 } { "new-wallet" } cond constant file-base
."Creating new wallet in workchain " wc . cr
// Create new simple wallet
<{ SETCP0 DUP IFNOTRET // return if recv_internal
DUP 85143 INT EQUAL IFJMP:<{ // "seqno" get-method
DROP c4 PUSHCTR CTOS 32 PLDU // cnt
}>
INC 32 THROWIF // 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:<{
// 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 coins from the balance
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 // >libref
// code
<b 0 32 u,
file-base +".pk" load-generate-keypair
constant wallet_pk
B,
b> // data
null // no libraries
// Libs{ x{ABACABADABACABA} drop x{AAAA} s>c public_lib x{1234} x{5678} |_ s>c public_lib }Libs
<b b{0011} s, 3 roll ref, rot ref, swap dict, b> // create StateInit
dup ."StateInit: " <s csr. cr
dup hash wc swap 2dup 2constant wallet_addr
."new wallet address = " 2dup .addr cr
2dup file-base +".addr" save-address-verbose
."Non-bounceable address (for init): " 2dup 7 .Addr cr
."Bounceable address (for later access): " 6 .Addr cr
<b 0 32 u, b>
dup ."signing message: " <s csr. cr
dup hash wallet_pk ed25519_sign_uint rot
<b b{1000100} s, wallet_addr addr, 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
file-base +"-query.boc" tuck B>file
."(Saved wallet creating query to file " type .")" cr
(您的发行版中的实际源文件可能略有不同。)本质上,它是一个完整的 Fift 脚本,用于创建由新生成的密钥对控制的智能合约的新实例。该脚本接受命令行参数,因此您无需在每次要创建新钱包时编辑源文件。
现在,如果您已经编译了 Fift 二进制文件(通常位于crypto/fift
构建目录中),您可以运行
$ crypto/fift -I<source-directory>/crypto/fift/lib -s <source-directory>/crypto/smartcont/new-wallet.fif 0 my_wallet_name
其中 0 是包含新钱包的工作链(0 = basechain,-1 = masterchain),my_wallet_name
是您希望与此钱包关联的任何标识符。新钱包的地址会被保存到 filemy_wallet_name.addr
中,新生成的私钥会被保存到my_wallet_name.pk
(除非这个文件已经存在,否则会从这个文件中加载密钥),外部消息会被保存到my_wallet_name-query.boc
. 如果您没有指明您的钱包的名称(my_wallet_name
在上面的示例中),则使用默认名称new-wallet
。
您可以选择将FIFTPATH
环境变量分别设置为<source-directory>/crypto/fift/lib:<source-directory>/crypto/smartcont
、包含Fift.fif
和Asm.fif
库文件的目录以及示例智能合约源;那么你可以省略-I
Fift 解释器的参数。如果您将 Fift 二进制文件安装crypto/fift
到包含在您的PATH
(例如,/usr/bin/fift
)中的目录,您可以简单地调用
$ fift -s new-wallet.fif 0 my_wallet_name
而不是在命令行中指示完整的搜索路径。
如果一切正常,您将看到如下内容
Creating new wallet in workchain 0
Saved new private key to file my_wallet_name.pk
StateInit: x{34_}
x{FF0020DD2082014C97BA9730ED44D0D70B1FE0A4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54}
x{00000000C59DC52962CC568AC5E72735EABB025C5BDF457D029AEEA6C2FFA5EB2A945446}
new wallet address = 0:2ee9b4fd4f077c9b223280c35763df9edab0b41ac20d36f4009677df95c3afe2
(Saving address to file my_wallet_name.addr)
Non-bounceable address (for init): 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb
Bounceable address (for later access): kQAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4rie
signing message: x{00000000}
External message for initialization is x{88005DD369FA9E0EF93644650186AEC7BF3DB5616835841A6DE8012CEFBF2B875FC41190260D403E40B2EE8BEB2855D0F4447679D9B9519BE64BE421166ABA2C66BEAAAF4EBAF8E162886430243216DDA10FCE68C07B6D7DDAA3E372478D711E3E1041C00000001_}
x{FF0020DD2082014C97BA9730ED44D0D70B1FE0A4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54}
x{00000000C59DC52962CC568AC5E72735EABB025C5BDF457D029AEEA6C2FFA5EB2A945446}
B5EE9C724104030100000000E50002CF88005DD369FA9E0EF93644650186AEC7BF3DB5616835841A6DE8012CEFBF2B875FC41190260D403E40B2EE8BEB2855D0F4447679D9B9519BE64BE421166ABA2C66BEAAAF4EBAF8E162886430243216DDA10FCE68C07B6D7DDAA3E372478D711E3E1041C000000010010200A2FF0020DD2082014C97BA9730ED44D0D70B1FE0A4F260810200D71820D70B1FED44D0D31FD3FFD15112BAF2A122F901541044F910F2A2F80001D31F3120D74A96D307D402FB00DED1A4C8CB1FCBFFC9ED54004800000000C59DC52962CC568AC5E72735EABB025C5BDF457D029AEEA6C2FFA5EB2A945446BCF59C17
(Saved wallet creating query to file my_wallet_name-query.boc)
简而言之,Fift 汇编器(由Asm.fif
包含行加载)用于将智能合约的源代码(包含在行中)编译<{ SETCP0 ... c4 POPCTR }>
为其内部表示。智能合约的初始数据也被创建(按<b 0 32 u, ... b>
行),包含一个 32 位序列号(等于零)和一个来自新生成的 Ed25519 密钥对的 256 位公钥。除非它已经存在,否则相应的私钥将保存到文件my_wallet_name.pk
中(如果您在同一目录中运行此代码两次,则将从该文件加载私钥)。
将新智能合约的代码和数据组合成一个StateInit
结构(在下一行),计算并输出新智能合约的地址(等于该StateInit
结构的哈希),然后是带有目的地的外部消息创建与新智能合约地址相同的地址。此外部消息包含StateInit
新智能合约的正确信息和重要的有效负载(由正确的私钥签名)。
最后,将外部消息序列化为一袋单元格(由 表示B5EE...BE63
)并保存到文件my_wallet_name-query.boc
中。本质上,此文件是您编译的智能合约,其中包含将其上传到 TON 区块链所需的所有附加信息。
4. 将部分资金转入新的智能合约
您可以尝试通过运行 Lite Client 并键入来立即上传新的智能合约
> sendfile new-wallet-query.boc
或者
> sendfile my_wallet_name-query.boc
如果你选择命名你的钱包my_wallet_name
。
不幸的是,这行不通,因为智能合约必须有一个正余额才能支付在区块链中存储和处理数据的费用。因此,您必须首先将一些资金转移到您的新智能合约地址,在其生成期间显示为-1:60c0...c0d0
(以原始形式)和0f9..EKD
(以用户友好的形式)。
在实际场景中,您可以从现有钱包中转移一些 TON 币,请朋友这样做,或者在加密货币交易所购买一些 TON 币,指示0f9...EKD
将新 TON 币转移到的帐户。
在测试网络中,您还有另一个选择:您可以要求“测试提供者”给您一些测试 TON 硬币(最多 20 个)。让我们解释一下如何做到这一点。
5. 使用测试提供者智能合约
你需要知道测试者智能合约的地址。我们将假设它是-1:fcb91a3a3816d0f7b8c2c76108b8a9bc5a6b7a55bd79f8ab101c52db29232260
,或者等价地,kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny
如前面的示例之一所示。您可以在 Lite Client 中通过键入来检查此智能合约的状态
> last
> getaccount kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny
如上文第 2 节所述。输出中唯一需要的数字是存储在智能合约数据中的 32 位序列号(0x9A15
在上面的示例中,但通常会有所不同)。获取此序列号当前值的更简单方法是键入
> last
> runmethod kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny seqno
产生正确的值 39445 = 0x9A15:
got account state for -1 : FCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260 with respect to blocks (-1,8000000000000000,2240):18E6DA7707191E76C71EABBC5277650666B7E2CFA2AEF2CE607EAFE8657A3820:4EFA2540C5D1E4A1BA2B529EE0B65415DF46BFFBD27A8EB74C4C0E17770D03B1
creating VM
starting VM to run method `seqno` (85143) of smart contract -1:FCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260
...
arguments: [ 85143 ]
result: [ 39445 ]
接下来,您向测试提供者创建一条外部消息,要求它向您的(未初始化的)智能合约发送另一条消息,其中包含指定数量的测试 TON 币。有一个特殊的 Fift 脚本用于生成此外部消息,位于crypto/smartcont/testgiver.fif
:
#!/usr/bin/env fift -s
"TonUtil.fif" include
{ ."usage: " @' $0 type ." <dest-addr> <seqno> <amount> [<savefile>]" cr
."Creates a request to TestGiver and saves it into <savefile>.boc" cr
."('testgiver-query.boc' by default)" cr 1 halt
} : usage
$# 3 - -2 and ' usage if
// "testgiver.addr" load-address
Masterchain 0xfcb91a3a3816d0f7b8c2c76108b8a9bc5a6b7a55bd79f8ab101c52db29232260
2constant giver_addr
."Test giver address = " giver_addr 2dup .addr cr 6 .Addr cr
$1 true parse-load-address =: bounce 2=: dest_addr
$2 parse-int =: seqno
$3 $>GR =: amount
def? $4 { @' $4 } { "testgiver-query" } cond constant savefile
."Requesting " amount .GR ."to account "
dest_addr 2dup bounce 7 + .Addr ." = " .addr
."seqno=0x" seqno x. ."bounce=" bounce . cr
// create a message (NB: 01b00.., b = bounce)
<b b{01} s, bounce 1 i, b{000100} s, dest_addr addr,
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, giver_addr addr, 0 Gram, b{00} s,
swap <s s, b>
dup ."resulting external message: " <s csr. cr
2 boc+>B dup Bx. cr
savefile +".boc" tuck B>file
."(Saved to file " type .")" cr
您可以将所需参数作为命令行参数传递给此脚本
$ crypto/fift -I<include-path> -s <path-to-testgiver-fif> <dest-addr> <testgiver-seqno> <coins-amount> [<savefile>]
例如,
$ crypto/fift -I<source-directory>/crypto/fift/lib:<source-directory>/crypto/smartcont -s testgiver.fif 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb 0x9A15 6.666 wallet-query
或者干脆
$ fift -s testgiver.fif 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb 0x9A15 6.666 wallet-query
前提是您已将环境变量设置FIFTPATH
为<source-directory>/crypto/fift/lib:<source-directory>/crypto/smartcont
并将 fift 二进制文件安装为/usr/bin/fift
(或您的PATH
.
新创建的发送到新智能合约的消息必须明确其退回位,否则传输将被“退回”给它的发送者。这就是我们通过0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb
新钱包智能合约的“不可反弹”地址的原因。
这个 Fift 代码创建了一条从测试提供者智能合约到我们的新智能合约地址的内部消息,该智能合约带有 6.666 个测试 TON 币(您可以在此处输入任何其他数量,最多大约 20 个 TON 币)。然后这个消息被封装成一个外部消息,发送给测试者;此外部消息还必须包含测试者的正确序列号。当测试提供者收到这样的外部消息时,它会检查序列号是否与存储在其持久数据中的序列号匹配,如果匹配,则将包含所需数量的测试 TON 币的嵌入式内部消息发送到其目的地(我们的智能合约在这种情况下)。
外部消息被序列化并保存到文件wallet-query.boc
中。在这个过程中会产生一些输出:
Test giver address = -1:fcb91a3a3816d0f7b8c2c76108b8a9bc5a6b7a55bd79f8ab101c52db29232260
kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny
Requesting GR$6.666 to account 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb = 0:2ee9b4fd4f077c9b223280c35763df9edab0b41ac20d36f4009677df95c3afe2 seqno=0x9a15 bounce=0
enveloping message: x{00009A1501}
x{42001774DA7EA783BE4D91194061ABB1EFCF6D585A0D61069B7A004B3BEFCAE1D7F1280C6A98B4000000000000000000000000000047494654}
resulting external message: x{89FF02ACEEB6F264BCBAC5CE85B372D8616CA2B4B9A5E3EC98BB496327807E0E1C1A000004D0A80C_}
x{42001774DA7EA783BE4D91194061ABB1EFCF6D585A0D61069B7A004B3BEFCAE1D7F1280C6A98B4000000000000000000000000000047494654}
B5EE9C7241040201000000006600014F89FF02ACEEB6F264BCBAC5CE85B372D8616CA2B4B9A5E3EC98BB496327807E0E1C1A000004D0A80C01007242001774DA7EA783BE4D91194061ABB1EFCF6D585A0D61069B7A004B3BEFCAE1D7F1280C6A98B4000000000000000000000000000047494654AFC17FA4
(Saved to file wallet-query.boc)
6. 将外部消息上传到测试者智能合约
现在我们可以调用 Lite Client,检查 test giver 的状态(如果序列号发生了变化,我们的外部消息将失败),然后输入
> sendfile wallet-query.boc
我们将看到一些输出:
... external message status is 1
这意味着外部消息已被传递到收集器池。之后,其中一个整理者可能会选择将此外部消息包含在一个块中,从而为测试提供者智能合约创建一个交易来处理此外部消息。我们可以检查测试者的状态是否发生了变化:
> last
> getaccount kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny
(如果您忘记输入last
,您可能会看到测试提供者智能合约的未更改状态。)结果输出将是:
got account state for -1 : FCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260 with respect to blocks (-1,8000000000000000,2240):18E6DA7707191E76C71EABBC5277650666B7E2CFA2AEF2CE607EAFE8657A3820:4EFA2540C5D1E4A1BA2B529EE0B65415DF46BFFBD27A8EB74C4C0E17770D03B1
account state is (account
addr:(addr_std
anycast:nothing workchain_id:-1 address:xFCB91A3A3816D0F7B8C2C76108B8A9BC5A6B7A55BD79F8AB101C52DB29232260)
storage_stat:(storage_info
used:(storage_used
cells:(var_uint len:1 value:3)
bits:(var_uint len:2 value:707)
public_cells:(var_uint len:0 value:0)) last_paid:0
due_payment:nothing)
storage:(account_storage last_trans_lt:10697000003
balance:(currencies
grams:(nanograms
amount:(var_uint len:7 value:999993280210000))
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{00009A16}
))
library:hme_empty))))
x{CFF8156775B79325E5D62E742D9B96C30B6515A5CD2F1F64C5DA4B193C03F070E0D2068086C00000000000000009F65D110DC0E35F450FA914134_}
x{FF0020DDA4F260D31F01ED44D0D31FD166BAF2A1F80001D307D4D1821804A817C80073FB0201FB00A4C8CB1FC9ED54}
x{00000001}
您可能会注意到,存储在持久化数据中的序列号发生了变化(在我们的示例中为 0x9A16 = 39446),并且last_trans_lt
字段(该帐户最后一次交易的逻辑时间)增加了。
现在我们可以检查新智能合约的状态:
> getaccount 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb
or
> getaccount 0:2ee9b4fd4f077c9b223280c35763df9edab0b41ac20d36f4009677df95c3afe2
现在我们看到
got account state for 0:2EE9B4FD4F077C9B223280C35763DF9EDAB0B41AC20D36F4009677DF95C3AFE2 with respect to blocks (-1,8000000000000000,16481):890F4D549428B2929F5D5E0C5719FBCDA60B308BA4B907797C9E846E644ADF26:22387176928F7BCEF654411CA820D858D57A10BBF1A0E153E1F77DE2EFB2A3FB and (-1,8000000000000000,16481):890F4D549428B2929F5D5E0C5719FBCDA60B308BA4B907797C9E846E644ADF26:22387176928F7BCEF654411CA820D858D57A10BBF1A0E153E1F77DE2EFB2A3FB
account state is (account
addr:(addr_std
anycast:nothing workchain_id:0 address:x2EE9B4FD4F077C9B223280C35763DF9EDAB0B41AC20D36F4009677DF95C3AFE2)
storage_stat:(storage_info
used:(storage_used
cells:(var_uint len:1 value:1)
bits:(var_uint len:1 value:111)
public_cells:(var_uint len:0 value:0)) last_paid:1553210152
due_payment:nothing)
storage:(account_storage last_trans_lt:16413000004
balance:(currencies
grams:(nanograms
amount:(var_uint len:5 value:6666000000))
other:(extra_currencies
dict:hme_empty))
state:account_uninit))
x{CFF60C04141C6A7B96D68615E7A91D265AD0F3A9A922E9AE9C901D4FA83F5D3C0D02025BC2E4A0D9400000000F492A0511406354C5A004_}
我们的新智能合约有一些正余额(6.666 个测试 TON 币),但没有代码或数据(由 反映state:account_uninit
)。
7.上传新智能合约的代码和数据
现在您终于可以上传带有StateInit
新智能合约的外部消息,其中包含其代码和数据:
> sendfile my_wallet_name-query.boc
... external message status is 1
> last
...
> getaccount 0QAu6bT9Twd8myIygMNXY9-e2rC0GsINNvQAlnfflcOv4uVb
...
got account state for 0:2EE9B4FD4F077C9B223280C35763DF9EDAB0B41AC20D36F4009677DF95C3AFE2 with respect to blocks (-1,8000000000000000,16709):D223B25D8D68401B4AA19893C00221CF9AB6B4E5BFECC75FD6048C27E001E0E2:4C184191CE996CF6F91F59CAD9B99B2FD5F3AA6F55B0B6135069AB432264358E and (-1,8000000000000000,16709):D223B25D8D68401B4AA19893C00221CF9AB6B4E5BFECC75FD6048C27E001E0E2:4C184191CE996CF6F91F59CAD9B99B2FD5F3AA6F55B0B6135069AB432264358E
account state is (account
addr:(addr_std
anycast:nothing workchain_id:0 address:x2EE9B4FD4F077C9B223280C35763DF9EDAB0B41AC20D36F4009677DF95C3AFE2)
storage_stat:(storage_info
used:(storage_used
cells:(var_uint len:1 value:3)
bits:(var_uint len:2 value:963)
public_cells:(var_uint len:0 value:0)) last_paid:1553210725
due_payment:nothing)
storage:(account_storage last_trans_lt:16625000002
balance:(currencies
grams:(nanograms
amount:(var_uint len:5 value:5983177000))
other:(extra_currencies
dict:hme_empty))
state:(account_active
(
split_depth:nothing
special:nothing
code:(just
value:(raw@^Cell
x{}
x{FF0020DDA4F260810200D71820D70B1FED44D0D7091FD709FFD15112BAF2A122F901541044F910F2A2F80001D7091F3120D74A97D70907D402FB00DED1A4C8CB1FCBFFC9ED54}
))
data:(just
value:(raw@^Cell
x{}
x{00000001F61CF0BC8E891AD7636E0CD35229D579323AA2DA827EB85D8071407464DC2FA3}
))
library:hme_empty))))
x{CFF60C04141C6A7B96D68615E7A91D265AD0F3A9A922E9AE9C901D4FA83F5D3C0D020680F0C2E4A0EB280000000F7BB57909405928024A134_}
x{FF0020DDA4F260810200D71820D70B1FED44D0D7091FD709FFD15112BAF2A122F901541044F910F2A2F80001D7091F3120D74A97D70907D402FB00DED1A4C8CB1FCBFFC9ED54}
x{00000001F61CF0BC8E891AD7636E0CD35229D579323AA2DA827EB85D8071407464DC2FA3}
您会看到智能合约已经使用来自StateInit
外部消息的代码和数据进行了初始化,并且由于处理费用,其余额略有减少。sendfile
现在它已启动并运行,您可以通过生成新的外部消息并使用Lite 客户端的命令将它们上传到 TON 区块链来激活它。
8. 使用简单钱包智能合约
实际上,本示例中使用的简单钱包智能合约可用于将测试 TON 币转移到任何其他账户。在这方面类似于上面讨论的测试提供者智能合约,不同之处在于它只处理由正确私钥(其所有者)签名的外部消息。在我们的例子中,它是在智能合约编译期间保存到文件中的私钥my_wallet_name.pk
(参见第 3 节)。
示例文件中提供了如何使用此智能合约的示例crypto/smartcont/wallet.fif
:
#!/usr/bin/env fift -s
"TonUtil.fif" include
{ ."usage: " @' $0 type ." <filename-base> <dest-addr> <seqno> <amount> [-B <body-boc>] [<savefile>]" cr
."Creates a request to simple wallet created by new-wallet.fif, with private key loaded from file <filename-base>.pk "
."and address from <filename-base>.addr, and saves it into <savefile>.boc ('wallet-query.boc' by default)" cr 1 halt
} : usage
$# dup 4 < swap 5 > or ' usage if
def? $6 { @' $5 "-B" $= { @' $6 =: body-boc-file [forget] $6 def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond
@' $# 2- =: $# } if } if
true constant bounce
$1 =: file-base
$2 bounce parse-load-address =: bounce 2=: dest_addr
$3 parse-int =: seqno
$4 $>GR =: amount
def? $5 { @' $5 } { "wallet-query" } cond constant savefile
file-base +".addr" load-address
2dup 2constant wallet_addr
."Source wallet address = " 2dup .addr cr 6 .Addr cr
file-base +".pk" load-keypair nip constant wallet_pk
def? body-boc-file { @' body-boc-file file>B B>boc } { <b "TEST" $, b> } cond
constant body-cell
."Transferring " amount .GR ."to account "
dest_addr 2dup bounce 7 + .Addr ." = " .addr
."seqno=0x" seqno x. ."bounce=" bounce . cr
."Body of transfer message is " body-cell <s csr. cr
// create a message
<b b{01} s, bounce 1 i, b{000100} s, dest_addr addr, amount Gram, 0 9 64 32 + + 1+ u,
body-cell <s 2dup s-fits? not rot over 1 i, -rot { drop body-cell ref, } { s, } cond
b>
<b seqno 32 u, 1 8 u, swap ref, b>
dup ."signing message: " <s csr. cr
dup hash wallet_pk ed25519_sign_uint
<b b{1000100} s, wallet_addr addr, 0 Gram, b{00} s,
swap B, swap <s s, b>
dup ."resulting external message: " <s csr. cr
2 boc+>B dup Bx. cr
savefile +".boc" tuck B>file
."(Saved to file " type .")" cr
您可以按如下方式调用此脚本:
$ fift -I<source-directory>/crypto/fift/lib:<source-directory>/crypto/smartcont -s wallet.fif <your-wallet-id> <destination-addr> <your-wallet-seqno> <coins-amount>
或者干脆
$ fift -s wallet.fif <your-wallet-id> <destination-addr> <your-wallet-seqno> <coins-amount>
如果您已正确设置PATH
和FIFTPATH
.
例如,
$ fift -s wallet.fif my_wallet_name kf8Ty2EqAKfAksff0upF1gOptUWRukyI9x5wfgCbh58Pss9j 1 .666
这my_wallet_name
是您之前使用的钱包的标识符new-wallet.fif
;测试钱包的地址和私钥将从文件my_wallet_name.addr
和my_wallet_name.pk
当前目录中加载。
当您运行此代码时(通过调用 Fift 解释器),您将创建一个外部消息,其目的地等于您的钱包智能合约的地址,其中包含正确的 Ed25519 签名、序列号和来自您的智能钱包的封装内部消息合约到指定的智能合约dest_addr
,附加任意值和任意有效载荷。当您的智能合约接收并处理此外部消息时,它首先会检查签名和序列号。如果它们是正确的,它将接受外部消息,将嵌入的内部消息从自身发送到预期的目的地,并增加其持久数据中的序列号(这是防止重放攻击的简单措施,以防此示例钱包智能合约代码最终用于真正的钱包应用程序)。
当然,真正的 TON 区块链钱包应用程序会隐藏上述所有中间步骤。它将首先将新智能合约的地址传达给用户,要求他们从另一个钱包或加密货币交易所将一些资金转移到指定的地址(以不可反弹的用户友好形式显示),然后提供一个显示当前余额并将资金转移到用户想要的任何其他地址的简单界面。(本文档的目的是解释如何创建新的重要智能合约并尝试使用 TON 区块链测试网络,而不是解释如何使用 Lite 客户端而不是更用户友好的钱包应用程序。)
最后一点:上述示例在基本工作链(工作链 0)中使用了智能合约。如果将工作链标识符 -1 而不是 0 作为第一个参数传递给new-wallet.fif
. 唯一不同的是,基础工作链中的处理和存储费用比主链中低 100-1000 倍。一些智能合约(例如验证者选举智能合约)仅接受来自主链智能合约的转账,因此如果您希望代表自己的验证者进行质押并参与选举,则需要主链中的钱包。