您好,欢迎进入天顺平台注册地址!

全国咨询热线

020-88888888

您的位置: 主页 > 新闻中心 > 行业动态

‘天顺娱乐’CKB 脚本编程简介第二弹:脚本基础

发布日期:2025-07-08 07:22浏览次数:
本文摘要:Xuejie 是 CKB-VM 的核心开发者,他在自己的博客「Less is More」中,创作了一系列讲解 CKB 脚本编程的文章,用作补足白皮书中撰写 CKB 脚本所需的所有缺陷的细节构建。

Xuejie 是 CKB-VM 的核心开发者,他在自己的博客「Less is More」中,创作了一系列讲解 CKB 脚本编程的文章,用作补足白皮书中撰写 CKB 脚本所需的所有缺陷的细节构建。本文是该系列的第二篇,详尽介绍了如何将脚本代码部署到 CKB 网络上,慢来查阅吧在上一篇文章里,我讲解了当前 CKB 的检验模型。这一篇不会更为有意思一点,我们要向大家展出如何将脚本代码确实部署到 CKB 网络上去。

我期望在你看完了本文后,你可以有能力自行去探寻 CKB 的世界并按照你自己的意愿去撰写新的脚本代码。但是请注意,尽管我坚信目前的 CKB 的编程模型早已比较平稳了,但是研发仍在展开中,因此未来还可能会有一些变化。我将竭力保证本文一直正处于近期的状态,但是如果在过程到任何困惑,本文以此版本下的 CKB 作为依据:https://github.com/nervosnetwork/ckb/commit/80b51a9851b5d535625c5d144e1accd38c32876bTips:这是一篇很长的文章,因为我想要为下周更加有意思的话题获取充裕的内容。

所以如果你时间不过于不够,你不用立刻读过。我会试着把它分为几个独立国家的部分,这样你就可以一次读书其中一部分。语法在开始之前,我们再行来区分两个术语:脚本(Script)和脚本代码(Script Code)。在本文以及整个系列文章内,我们将区分脚本和脚本代码。

脚本代码实质上是所指你撰写和编译器并在 CKB 上运营的程序。而脚本,实质上是指 CKB 中用于的脚本数据结构,它不会比脚本代码略为多一点点:pub struct Script {pub args: VecBytes,pub code_hash: H256,pub hash_type: ScriptHashType,}我们目前可以再行忽视 hash_type,之后的文章再行来说明什是 hash_type 以及它有什么有意思的用法。

在这篇文章的后面,我们不会解释 code_hash 实质上是用来标识脚本代码的,所以目前我们可以只把它当作脚本代码。那脚本还包括什么呢?脚本还包括 args 这个部分,它是用来区分脚本和脚本代码的。args 在这里可以用来给一个 CKB 脚本获取额外的参数,比如:虽然大家有可能都会用于完全相同的配置文件的 Lock Script Code,但是每个人有可能都有自己的 Pubkey Hash,args 就是用来留存 Pubkey Hash 的方位。

这样,每一个CKB 的用户都可以享有有所不同的 Lock Script ,但是却可以共用某种程度的 Lock Script Code。请注意,在大多数情况下,脚本和脚本代码是可以交换用于的,但是如果你在某些地方深感了疑惑,那么你有可能有适当考虑一下两者间的区别。

一个大于的 CKB 脚本代码有可能你之前听闻过,CKB-VM 是基于开源的 RISC-V 指令集撰写的。但这究竟意味著什么呢?用我自己的话来说,这意味著我们(在或许上)在 CKB 中映射了一台确实的微型计算机,而不是一台虚拟机。

一台确实的计算机的益处是,你可以用任何语言撰写任何你想写的逻辑。在这里,网卓新闻网,我们不会再行展出几个用 C 语言撰写的例子,以维持非常简单性(我是说道工具链中的非常简单性,而不是语言),之后我们还不会转换到基于 JavaScript 的脚本代码,并期望在本系列中展出更好的语言。忘记,在 CKB 上有无限的有可能!正如我们提及的,CKB-VM 更加看起来一台确实的微型计算机。CKB 的代码脚本看上去也更加看起来我们在电脑上跑的一个少见的 Unix 风格的可执行程序。

int main(int argc, char* argv[]){ return 0;}当你的代码通过 C 编译器编译器时,它将沦为可以在 CKB 上运营的脚本代码。换句话说,CKB 只是使用了普通的旧式 Unix 风格的可执行程序 (但用于的是 RISC-V 体系结构,而不是风行的 x86 体系结构),并在虚拟机环境中运行它。如果程序的回到代码是 0 ,我们指出脚本顺利了,所有非零的回到代码都将被视作告终脚本。

在上面的例子中,我们展出了一个总是顺利的脚本代码。因为回到代码总是 0。但是请求不要用于这个作为您的 Lock Script Code ,否则您的 Token 可能会被任何人偷走。

但是似乎,上面的例子并不有意思,这里我们从一个有意思的点子开始:我个人不是很讨厌胡萝卜。我告诉胡萝卜从营养的角度来看是很好的,但我还是不讨厌它的味道。如果现在我想要原作一个规则,比如我想要让我在 CKB 上的 Cell 里面都没以 carrot 结尾的数据,这该怎么构建?让我们撰写一个脚本代码来试试。为了保证没一个 Cell 在 Cell Data 中包括 carrot,我们首先必须一种方法来加载脚本中的 Cell Data。

CKB 获取了 syscalls 来协助解决问题这个问题。为了保证 CKB 脚本的安全性,每个脚本都必需在与运营 CKB 的主计算机几乎分离出来的隔绝环境中运行。这样它就无法采访它不必须的数据,比如你的私钥或密码。

然而,想让脚本简单,必需要采访特定的数据,比如脚本维护的 cell 或脚本检验。CKB 获取了 syscalls 来保证这一点,syscalls 是在 RISC-V 的标准中定义的,它们获取了采访环境中某些资源的方法。

在长时间情况下,这里的环境所指的是操作系统,但是在 CKB VM 中,环境所指的是实际的 CKB 进程。用于 syscalls, CKB脚本可以采访包括自身的整个数据,还包括输出(Inputs)、输入(Outpus)、亲眼(Witnesses)和 Deps。更加篮的是,我们早已将 syscalls PCB在了一个更容易用于的头文件中,十分欢迎您在这里查阅这个文件,理解如何构建 syscalls:https://github.com/nervosnetwork/ckb-system-scripts/blob/66d7da8ec72dffaa7e9c55904833951eca2422a9/c/ckb_syscalls.h最重要的是,您可以只提供这个头文件并用于纸盒函数来创立您想的系统调用。

现在有了 syscalls,我们可以从禁令用于 carrot 的脚本开始:#include memory.h#include "ckb_syscalls.h"int main(int argc, char* argv[]) { int ret; size_t index = 0; volatile uint64_t len = 0; /* (1) */ unsigned char buffer[6]; while (1) {len = 6;memset(buffer, 0, 6);ret = ckb_load_cell_by_field(buffer, len, 0, index, CKB_SOURCE_OUTPUT,CKB_CELL_FIELD_DATA); /* (2) */if (ret == CKB_INDEX_OUT_OF_BOUND) { /* (3) */ break;}if (memcmp(buffer, "carrot", 6) == 0) { return -1;}index++; } return 0;}以下几点必须解释一下:1. 由于 C 语言的怪癖,len 字段必须标记为 volatile。我们不会同时用于它作为输出和输入参数,CKB-VM 不能在它还留存在内存中时,才可以把它设置为输入参数。而 volatile 可以保证 C 编译器将它留存为基于 RISC-V 内存的变量。2. 在用于 syscall 时,我们必须获取以下功能:一个缓冲区来留存 syscall 获取的数据;一个 len 字段,来回应系统调用回到的缓冲区长度和能用数据长度;一个输出数据缓冲区中的偏移量,以及几个我们在交易中必须提供的清楚字段的参数。

下文请参阅我们的 RFC:https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0009-vm-syscalls/0009-vm-syscalls.md3. 为了确保仅次于的灵活性,CKB 用于系统调用的返回值来回应数据捕捉状态:0 (or CKB_SUCCESS) 意味著顺利,1(or CKB_INDEX_OUT_OF_BOUND)意味著您早已通过一种方式提供了所有的索引,2 (or CKB_ITEM_MISSING) 意味著不不存在一个实体,比如从一个不包括该 Type 脚本的 Cell 中提供该 Type 的脚本。总结一下,这个脚本将循环迭代交易中的所有输入 Cell,读取每个 Cell Data 的前 6 个字节,并测试这些字节否和 carrot 给定。如果寻找给定,脚本将回到 -1,回应错误状态;如果没寻找给定,脚本将回到 0 解散,回应继续执行顺利。为了继续执行该循环,该脚本将留存一个 index 变量,在每次循环递归中,它将企图让 Syscall 提供 Cell 中目前使用的 index 值,如果 Syscall 回到 CKB_INDEX_OUT_OF_BOUND,这意味著脚本早已迭代所有的 Cell,之后不会解散循环;否则,循环将之后,每测试 Cell Data 一次,index 变量就不会递减一次。

这是第一个简单的 CKB 脚本代码!在下一节中,我们将展出如何将其部署到 CKB 中并运营它。将脚本部署到 CKB 上首先,我们必须编译器上面写出的关于胡萝卜的源代码。由于 GCC 早已获取了 RISC-V 的反对,您可以用于官方的 GCC 来创立脚本代码。或者你也可以用于我们打算的 Docker 镜像(https://hub.docker.com/r/nervos/ckb-riscv-gnu-toolchain)来防止编译器 GCC 的困难:$ lscarrot.c ckb_consts.h ckb_syscalls.h$ sudo docker run --rm -it -v `pwd`:/code nervos/ckb-riscv-gnu-toolchain:xenial bash[emailprotected]:/# cd /code[emailprotected]:/code# riscv64-unknown-elf-gcc -Os carrot.c -o carrot[emailprotected]:/code# exitexit$ lscarrot* carrot.c ckb_consts.h ckb_syscalls.h就像上面展出的这样,CKB 可以必要用于 GCC 编译器的可执行文件作为链上的脚本,需要更进一步处置。

我们现在可以在链上部署它了。留意,我将用于 CKB 的 Ruby SDK,因为我曾多次是一名 Ruby 程序员,所以 Ruby 对我来说是最习惯的(但不一定是最差的)。如何设置请求参照官方 Readme 文件:https://github.com/nervosnetwork/ckb-sdk-ruby/blob/develop/README.md要将脚本部署到 CKB,我们只需创立一个新的 Cell,把脚本代码另设为 Cell Data 部分:pry(main) data = File.read("carrot")pry(main) data.bytesize= 6864pry(main) carrot_tx_hash = wallet.send_capacity(wallet.address, CKB::Utils.byte_to_shannon(8000), CKB::Utils.bin_to_hex(data))在这里,我首先要通过向自己发送到 Token 来创立一个容量充足的新的 Cell。

现在我们可以创立包括胡萝卜脚本代码的脚本:pry(main) carrot_data_hash = CKB::Blake2b.hexdigest(data)pry(main) carrot_type_script = CKB::Types::Script.new(code_hash: carrot_data_hash, args: [])回想一下脚本数据结构:pub struct Script {pub args: VecBytes,pub code_hash: H256,pub hash_type: ScriptHashType,}可以看见,我没必要将脚本代码映射到脚本数据结构中,而是只包括了代码的哈希,这是实际脚本二进制代码的 Blake2b 哈希。由于胡萝卜脚本不用于参数,我们可以对 args 部分用于空数组。留意,这里依然忽视了 hash_type,我们将在后面的文章中通过另一种方式辩论登录代码哈希。现在,让我们尽可能维持简练。

要运营胡萝卜脚本,我们必须创立一个新的交易,并将胡萝卜 Type 脚本设置为其中一个输入 Cell 的 Type 脚本:pry(main) tx = wallet.generate_tx(wallet2.address, CKB::Utils.byte_to_shannon(200))pry(main) tx.outputs[0].instance_variable_set(:@type, carrot_type_script.dup)我们还必须展开一个步骤:为了让 CKB 可以寻找胡萝卜脚本,我们必须在一笔交易的 Deps 中提到包括胡萝卜脚本的 Cell:pry(main) carrot_out_point = CKB::Types::OutPoint.new(cell: CKB::Types::CellOutPoint.new(tx_hash: carrot_tx_hash, index: 0))pry(main) tx.deps.push(carrot_out_point.dup)现在我们打算亲笔签名并发送到交易:[44] pry(main) tx.witnesses[0].data.clear[46] pry(main) tx = tx.sign(wallet.key, api.compute_transaction_hash(tx))[19] pry(main) api.send_transaction(tx)= "0xd7b0fea7c1527cde27cc4e7a2e055e494690a384db14cc35cd2e51ec6f078163"由于该交易的 Cell 中没任何一个的 Cell Data 包括 carrot,因此 Type 脚本将检验顺利。现在让我们尝试一个有所不同的交易,它显然所含一个以 carrot 结尾的 Cell:pry(main) tx2 = wallet.generate_tx(wallet2.address, CKB::Utils.byte_to_shannon(200))pry(main) tx2.deps.push(carrot_out_point.dup)pry(main) tx2.outputs[0].instance_variable_set(:@type, carrot_type_script.dup)pry(main) tx2.outputs[0].instance_variable_set(:@data, CKB::Utils.bin_to_hex("carrot123"))pry(main) tx2.witnesses[0].data.clearpry(main) tx2 = tx2.sign(wallet.key, api.compute_transaction_hash(tx2))pry(main) api.send_transaction(tx2)CKB::RPCError: jsonrpc error: {:code=-3, :message="InvalidTx(ScriptFailure(ValidationFailure(-1)))"}from /home/ubuntu/code/ckb-sdk-ruby/lib/ckb/rpc.rb:164:in `rpc_request'我们可以看见,胡萝卜脚本拒绝接受了一笔分解的 Cell 中包括胡萝卜的交易。现在我可以用于这个脚本来保证所有的 Cell 中都含胡萝卜!所以,总结一下,部署和运营一个 Type 脚本的脚本,我们必须做到的是:1. 将脚本编译器为 RISC-V 可继续执行的二进制文件;2. 在 Cell 的 Data 部分部署二进制文件;3. 创立一个 Type 脚本数据结构,用于二进制文件的 Blake2b 散列作为 code hash,补足 args 部分中脚本代码的必须的参数;4. 用分解的 Cell 中设置的 Type 脚本创立一个新的交易;5. 将包括脚本代码的 Cell 的 Outpoint 载入到一笔交易的 Deps 中去。

虽然在这里我们只辩论了 Type 脚本,但是 Lock 脚本的工作方式完全相同。你唯一必须忘记的是,当你用于特定的 Lock 脚本创立 Cell 时,Lock 脚本会在这里运营,它只在你用于 Cell 时运营。

因此, Type 脚本可以用作结构创立 Cell 时运营的逻辑,而 Lock 脚本用作结构封存 Cell 时运营的逻辑。考虑到这一点,请求保证你的 Lock 脚本是准确的,否则你可能会在以下场景中遗失 Token:您的 Lock 脚本有一个其他人也可以关卡你的 Cell 的 Bug。

您的 Lock 脚本有一个 任何人(还包括您)都无法关卡你的 Cell 的 Bug。在这里我可以获取一个技巧,一直将您的脚本作为一个 Type 脚本可选到你交易的一个 Output Cell 中去展开测试,这样,再次发生错误时,您可以立刻告诉,并且您的 Token 可以始终保持安全性。分析配置文件 Lock 脚本代码根据早已掌控的科学知识,让我们想到 CKB 中包括的配置文件的 Lock 脚本代码。为了防止误解,我们可以在这个 Commit 里查阅 Lock 脚本代码:https://github.com/nervosnetwork/ckb-system-scripts/blob/66e2b3fc4fa3e80235e4b4f94a16e81352a812f7/c/secp256k1_blake160_sighash_all.c配置文件的 Lock 脚本代码将循环迭代与自身具备完全相同 Lock 脚本的所有的 Input Cell,并继续执行以下步骤:它通过获取的 Syscall 提供当前的交易 Hash它提供适当的 Witness 数据作为当前输出对于配置文件 Lock 脚本,假设 Witness 中的第一个参数包括由 Cell 所有者亲笔签名的可恢复亲笔签名,其余参数是用户获取的附加参数配置文件的 Lock 脚本运营由交易 Hash 链接的二进制程序的 Blake2b Hash, 还有所有用户获取的参数(如果不存在的话)将 Blake2b Hash 结果用于 Secp256k1 亲笔签名检验的消息部分。

留意,Witness 数据结构中的第一个参数获取了实际的亲笔签名。如果亲笔签名检验告终,脚本解散并回到错误码。

否则它将之后下一个递归。留意,我们在前面辩论了脚本和脚本代码之间的区别。

每一个有所不同的公钥哈希都会产生有所不同的 Lock 脚本,因此,如果一个交易的输出 Cell 具备完全相同的配置文件 Lock 脚本代码,但具备有所不同的公钥哈希(因此具备有所不同的 Lock 脚本),将继续执行配置文件 Lock 脚本代码的多个实例,每个实例都有一组分享完全相同 Lock 脚本的 Cell。现在我们可以迭代配置文件 Lock 脚本代码的有所不同部分:if (argc != 2) { return ERROR_WRONG_NUMBER_OF_ARGUMENTS;}secp256k1_context context;if (secp256k1_context_initialize(context, SECP256K1_CONTEXT_VERIFY) == 0) { return ERROR_SECP_INITIALIZE;}len = BLAKE2B_BLOCK_SIZE;ret = ckb_load_tx_hash(tx_hash, len, 0);if (ret != CKB_SUCCESS) { return ERROR_SYSCALL;当参数包括在 Script 数据结构的 args 部分,它们通过 Unix 传统的 arc/ argv 方式发送给实际运营的脚本程序。为了更进一步维持誓约,我们在 argv[0] 处放入一个伪参数,所以,第一个包括的参数从 argv[1] 开始。

在配置文件 Lock 脚本代码的情况下,它拒绝接受一个参数,即从所有者的私钥分解的公钥 Hash。ret = ckb_load_input_by_field(NULL, len, 0, index, CKB_SOURCE_GROUP_INPUT,CKB_INPUT_FIELD_SINCE);if (ret == CKB_INDEX_OUT_OF_BOUND) { return 0;}if (ret != CKB_SUCCESS) { return ERROR_SYSCALL;}我们再行来看下胡萝卜这个例子,我们检查否有更加多的输出 Cell 要测试。

与之前的例子有两个有所不同:如果我们只想告诉一个 Cell 否不存在并且不必须任何数据,我们只必须起源于 NULL 作为数据缓冲区,一个 len 变量的值是 0。通过这种方式,Syscall 将跳过数据填满,只获取能用的数据长度和准确的回到码用作处置。

在这个胡萝卜的例子中,我们循环迭代交易中的所有输出,但这里我们只关心具备完全相同 Lock 脚本的输出 Cell。CKB 将具备完全相同瞄准(或类型)脚本的 Cell 命名为 group。

我们可以用于 CKB_SOURCE_GROUP_INPUT 替换 CKB_SOURCE_INPUT, 来回应只计算出来同一组中的 Cell,荐个例子,即具备与当前 Cell 完全相同的 Lock 脚本的 Cell。len = WITNESS_SIZE;ret = ckb_load_witness(witness, len, 0, index, CKB_SOURCE_GROUP_INPUT);if (ret != CKB_SUCCESS) {return ERROR_SYSCALL;}if (lenWITNESS_SIZE) {return ERROR_WITNESS_TOO_LONG;}if (!(witness_table = ns(Witness_as_root(witness)))) {return ERROR_ENCODING;}args = ns(Witness_data(witness_table));if (ns(Bytes_vec_len(args))1) {return ERROR_WRONG_NUMBER_OF_ARGUMENTS;}之后沿着这个路径,我们正在读取当前输出的 Witness。

对应的 Witness 和输出具备完全相同的索引。现在 CKB 在 Syscalls 中用于flatbuffer作为序列化格式,所以如果你很奇怪,Flatcc 的文档(https://github.com/dvidelabs/flatcc)可以协助你解读。/* Load signature */len = TEMP_SIZE;ret = extract_bytes(ns(Bytes_vec_at(args, 0)), temp, len);if (ret != CKB_SUCCESS) { return ERROR_ENCODING;}/* The 65th byte is recid according to contract spec.*/recid = temp[RECID_INDEX];/* Recover pubkey */secp256k1_ecdsa_recoverable_signature signature;if (secp256k1_ecdsa_recoverable_signature_parse_compact(context, signature, temp, recid) == 0) { return ERROR_SECP_PARSE_SIGNATURE;}blake2b_state blake2b_ctx;blake2b_init(blake2b_ctx, BLAKE2B_BLOCK_SIZE);blake2b_update(blake2b_ctx, tx_hash, BLAKE2B_BLOCK_SIZE);for (size_t i = 1; ins(Bytes_vec_len(args)); i++) { len = TEMP_SIZE; ret = extract_bytes(ns(Bytes_vec_at(args, i)), temp, len); if (ret != CKB_SUCCESS) {return ERROR_ENCODING; } blake2b_update(blake2b_ctx, temp, len);}blake2b_final(blake2b_ctx, temp, BLAKE2B_BLOCK_SIZE);Witness 中的第一个参数是要读取的亲笔签名,而其余的参数(如果获取的话)被可选到用作 Blake2b 操作者的交易 Hash 中。

secp256k1_pubkey pubkey;if (secp256k1_ecdsa_recover(context, pubkey, signature, temp) != 1) { return ERROR_SECP_RECOVER_PUBKEY;}然后用于哈希后的 Blake2b 结果作为信息,展开 Secp256 亲笔签名检验。size_t pubkey_size = PUBKEY_SIZE;if (secp256k1_ec_pubkey_serialize(context, temp, pubkey_size, pubkey, SECP256K1_EC_COMPRESSED) != 1 ) { return ERROR_SECP_SERIALIZE_PUBKEY;}len = PUBKEY_SIZE;blake2b_init(blake2b_ctx, BLAKE2B_BLOCK_SIZE);blake2b_update(blake2b_ctx, temp, len);blake2b_final(blake2b_ctx, temp, BLAKE2B_BLOCK_SIZE);if (memcmp(argv[1], temp, BLAKE160_SIZE) != 0) { return ERROR_PUBKEY_BLAKE160_HASH;}最后,某种程度最重要的是,我们还必须检查可恢复亲笔签名中包括的 Pubkey 显然是用作分解 Lock 脚本参数中包括的 Pubkey Hash 的 Pubkey。否则,可能会有人用于另一个公钥分解的亲笔签名来盗取你的 Token。简而言之,配置文件 Lock 脚本中用于的方案与现在比特币中用于的方案十分相近。

讲解 Duktape我坚信你和我现在的感觉一样:我们可以用 C 语言写出合约,这很好,但是 C 语言总是让人实在有点无趣,而且,更加现实一点说道,它很危险性。有更佳的方法吗?当然!我们上面提及的 CKB-VM 本质上是一台微型计算机,我们可以探寻很多解决方案。

我们现在做到的一件事是,用于 JavaScript 撰写 CKB 脚本代码。是的,你说道对了,非常简单的 ES5(是的,我告诉,但这只是一个例子,你可以用于转换器)JavaScript。这怎么有可能呢?由于我们有 C 编译器,我们只需为嵌入式系统用于一个 JavaScript 构建,在我们的例子中,Duktape 将它从 C 编译成 RISC-V 二进制文件,把它放到链上,我们就可以在 CKB 上运营 JavaScript 了!因为我们用于的是一台确实的微型计算机,所以没什么可以制止我们将另一个 VM 作为 CKB 脚本映射到 CKB-VM 中,并在 VM 路径上探寻这个 VM。

从这条路径进行,我们可以通过 Duktape 在 CKB 上用于 JavaScript,我们也可以通过 Mruby 在 CKB 上用于 Ruby, 我们甚至可以将比特币脚本或 EVM 放在链上,我们只必须编译器他们的虚拟机,并把它放到链上。这保证了 CKB-VM 既能协助我们留存资产,又能建构一个多样化的生态系统。所有的语言都应当在 CKB 上被公平对待,权利应当掌控在区块链合约的开发者手中。

在这个阶段,你有可能实在:是的,这是有可能的,但是 VM 之上的 VM 会极快吗?我坚信这各不相同你的例子否极快。我深信,基准测试没任何意义,除非我们将它放到具备标准硬件市场需求的实际用例中。所以我们必须让时间来检验这否知道不会沦为一个问题。

在我看来,高级语言更加有可能用作 Type Scripts 来维护 Cell 切换,在这种情况下,我猜测它不会极快。此外,我们也在这个领域希望,以优化 CKB-VM 和 VMs 之上的 CKB-VM,使其更加将要在 CKB 上用于 Duktape,首先必须将 Duktape 本身编译成 RISC-V 可继续执行二进制文件:$ git clone https://github.com/nervosnetwork/ckb-duktape$ cd ckb-duktape$ sudo docker run --rm -it -v `pwd`:/code nervos/ckb-riscv-gnu-toolchain:xenial bash[emailprotected]:~# cd /code[emailprotected]:/code# makeriscv64-unknown-elf-gcc -Os -DCKB_NO_MMU -D__riscv_soft_float -D__riscv_float_abi_soft -Iduktape -Ic -Wall -Werror c/entry.c -c -o build/entry.oriscv64-unknown-elf-gcc -Os -DCKB_NO_MMU -D__riscv_soft_float -D__riscv_float_abi_soft -Iduktape -Ic -Wall -Werror duktape/duktape.c -c -o build/duktape.oriscv64-unknown-elf-gcc build/entry.o build/duktape.o -o build/duktape -lm -Wl,-static -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,-s[emailprotected]:/code# exitexit$ ls build/duktapebuild/duktape*与胡萝卜的示例一样,这里的第一步是在 CKB Cell 中部署 Duktape 脚本代码:pry(main) data = File.read("../ckb-duktape/build/duktape")pry(main) duktape_data.bytesize= 269064pry(main) duktape_tx_hash = wallet.send_capacity(wallet.address, CKB::Utils.byte_to_shannon(280000), CKB::Utils.bin_to_hex(duktape_data))pry(main) duktape_data_hash = CKB::Blake2b.hexdigest(duktape_data)pry(main) duktape_out_point = CKB::Types::OutPoint.new(cell: CKB::Types::CellOutPoint.new(tx_hash: duktape_tx_hash, index: 0))但是,和和胡萝卜例子有所不同的是,Duktape 脚本代码现在必须一个参数:要继续执行的 JavaScript 源代码:pry(main) duktape_hello_type_script = CKB::Types::Script.new(code_hash: duktape_data_hash, args: [CKB::Utils.bin_to_hex("CKB.debug(\"I'm running in JS!\")")])留意,用于有所不同的参数,你可以为有所不同的用例创立有所不同的 Duktape 反对的 Type Script:pry(main) duktape_hello_type_script = CKB::Types::Script.new(code_hash: duktape_data_hash, args: [CKB::Utils.bin_to_hex("var a = 1;\nvar b = a + 2;")])这体现了上面提及的脚本代码与脚本之间的差异:这里 Duktape 作为获取 JavaScript 引擎的脚本代码,而有所不同的脚本利用 Duktape 脚本代码在链上获取有所不同的功能。

现在我们可以创立一个 Cell 与 Duktape 的 Type Script 附件:pry(main) tx = wallet.generate_tx(wallet2.address, CKB::Utils.byte_to_shannon(200))pry(main) tx.deps.push(duktape_out_point.dup)pry(main) tx.outputs[0].instance_variable_set(:@type, duktape_hello_type_script.dup)pry(main) tx.witnesses[0].data.clearpry(main) tx = tx.sign(wallet.key, api.compute_transaction_hash(tx))pry(main) api.send_transaction(tx)= "0x2e4d3aab4284bc52fc6f07df66e7c8fc0e236916b8a8b8417abb2a2c60824028"我们可以看见脚本继续执行顺利,如果在 ckb.toml 文件中将 ckb-script 日志模块的级别设置为 debug,你可以看见以下日志:2019-07-15 05:59:13.551 +00:00 http.worker8 DEBUG ckb-script script group: c35b9fed5fc0dd6eaef5a918cd7a4e4b77ea93398bece4d4572b67a474874641 DEBUG OUTPUT: I'm running in JS!现在你早已顺利地在 CKB 上部署了一个 JavaScript 引擎,并在 CKB 上运营基于 JavaScript 的脚本!你可以在这里尝试了解的 JavaScript 代码。一道思考题现在你早已熟知了 CKB 脚本的基础知识,下面是一个思维:在本文中,您早已看见了一个 Always-success 的脚本是什么样子的,但是一个 Always-failure 的脚本呢?一个 Always-failure 脚本(和脚本代码)能有多小?提醒:这不是 GCC 优化比赛,这只是一个思维。

上集预告片我告诉这是一个很长的帖子,我期望你早已尝试过并顺利地部署了一个脚本到 CKB。在下一篇文章中,我们将讲解一个最重要的主题:如何在 CKB 中让自己的用户定义 Token(UDT)。CKB 上 UDT 最差的部分是,每个用户都可以将自己的 UDT 存储在自己的 Cell 中,这与 Ethereum 上的 ERC20 令牌有所不同,在 Ethereum 上,每个人的 Token 都必需坐落于 Token 发起者的单个地址中。

所有这些都可以通过分开用于 Type Script 来构建。


本文关键词:天顺平台注册地址,电子娱乐平台游戏,天顺娱乐

本文来源:天顺平台注册地址-www.tqysc.com

Copyright © 2009-2025 www.tqysc.com. 天顺平台注册地址科技 版权所有备案号:ICP备77749471号-6网站地图

扫一扫咨询微信客服
020-88888888