网上有很多关于火星文带符号转换器,火星文字转换器的知识,也有很多人为大家解答关于火星文符号转换器的问题,今天刺梨占星网(nayona.cn)为大家整理了关于这方面的知识,让我们一起来看下吧!
本文目录一览:
1、火星文符号转换器
火星文符号转换器
免责声明:本文旨在传递更多市场信息,不构成任何投资建议。文章仅代表作者观点,不代表火星财经官方立场。
小编:记得关注哦
来源:NervosNetwork
Xuejie 是 CKB-VM的核心开发者,他在自己的博客「Less is More」中,创作了一系列介绍 CKB 脚本编程的文章,用于补充白皮书中编写 CKB 脚本所需de所有缺失的细节实现。本文是该系列的第二篇,详细讲解了如何将脚本代码部署到 CKB 网络上,快来查看吧 ~^.^~
作者:Xuejie
原文链接:https://xuejie.space/2019_07_13_introduction_to_ckb_Script_programming_script_basics/
译者:Shooter, Jason, Orange(排名不分先后)
校对:Joey
在上一篇文章里,我介绍了当前 CKB 的验证模型。这一篇会更加有趣一点,我们要向大家展示如何将脚本代码真正部署到 CKB 网络上去。我希望在你看完本文后,你可以有能力自行去探索 CKB 的世界并按照你自己的意愿去编写新的脚本代码。但是请注意,尽管我相信目前的 CKB 的编程模型已经相对稳定了,但是开发仍在进行中,因此未来还可能会有一些变化。我将尽力确保本文始终处于最新的状态,但是如果在过程到任何疑惑,本文以此版本下的 CKB 作为依据:
https://github.com/nervosnetwork/ckb/commit/80b51a9851b5d535625c5d144e1accd38c32876b
Tips:这是一篇很长的文章,因为我想为下周更有趣的话题提供充足的内容。所以如果你时间不太够,你不必马上读完。我会试着把它分成几个独立的部分,这样你就可以一次读其中一部fēn。
语法
在开始之前,我们先来区分两个术语:脚本(Script)和脚本代码(Script Code)。
在本文以及整个系列文章内,我们将区分脚本和脚本代码。脚本代码实际上是指你编写和编译并在 CKB 上运行的程序。而脚本,实际上是指 CKB 中使用的脚本数据结构,它会比脚本代码稍微多一点点:
pub struct Script { pub args: Vec
我们目前可以xiān忽略 hash_type,之后的文章再来解释什是 hash_type 以及它有什么有趣的用法。在这篇文章的后面,我们会说明 code_hash 实际上是用来标识脚本代码的,所以目前我们可以只把它当成脚本代码。那脚本还包括什么呢?脚本还包括 args 这个部分,它是用来区分脚本和脚本代码的。args 在这里可以用来给一个 CKB 脚本提供额外的参数,比如:虽然大家可能都会使用相同的默认的 Lock Script Code,但是每个人可能都有自己的 Pubkey Hash,args 就是用来保存 Pubkey Hash 的位置。这样,每一个CKB 的用户都可以拥有不同的 Lock Script ,但是却可以共用同样的 Lock Script Code。
请注意,在大多数情况下,脚本和脚本代码是可以互huàn使用的,但是如果你在某些地方感到了困惑,那么你可能有必要考虑一下两者间的区别。
一个最小的 CKB 脚本代码
可能你之前听说过,CKB-VM 是基于开源的 RISC-V 指令集编写的。但这到底意味着什么呢?用我自己的话来说,这意味着我们(在某种程度上)在 CKB 中嵌入了一tái真正的微型计算机,而不是一台虚拟机。一台真正的计算机的好处是,你可以用任何语言编写任何你想写的逻辑。在这里,我们会先展示几个用 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,我们首先需要一种方fǎ来读取脚本中的 Cell Data。CKB 提供了 syscalls 来帮助解决这个问题。为了确保 CKB 脚本的安全性,每个脚本都必须在与运行 CKB 的主计算机完全分离的隔离环境中运行。这样它就不能访问它不需要的数据,比如你的私钥或密码。然而,想要让脚本有用,必须要访问特定的数据,比如脚本保护的 cell 或脚本验证。CKB 提供了 syscalls 来确保这一点,syscalls 是在 RISC-V 的标准中定义的,它们提供了访问环境中某些资源的方法。在正常情况下,这里的环境指的是操作系统,但是在 CKB VM 中,环境指的是实际的 CKB 进程。使用 syscalls, CKB脚本可以访问包含自身的整个数据,包括输入(Inputs)、输出(Outpus)、见证(Witnesses)和 Deps。
更棒的是,我们已经将 syscalls 封装在了一个易于使用的头文件中,非常欢迎您在这里查看这个文件,了解如何实现 syscalls:
https://github.com/nervosnetwork/ckb-system-scripts/blob/66d7da8ec72dffaa7e9c55904833951eca2422a9/c/ckb_syscalls.h
最重要的是,您可以只获取这个头文件并使用包装函数来创建您想要的系统调用。现在有了 syscalls,我们可以从禁止使用 carrot 的脚本开始:
#include #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;}
以下几点需要解释一下:
由于 C 语言的怪癖,len 字段需要标记为 volatile。我们会同时使用它作为输入和输出参数,CKB-VM 只能在它还保存在内存中时,才可以把它设置为输出cān数。而 volatile 可以确保 C 编译器将它保存为基于 RISC-V 内存的变量。在使用 syscall 时,我们需要提供以下功能:一个缓冲区来bǎo存 syscall 提供的数据;一个 len 字段,来表示系统调用返回的缓冲区长度和可用数据长度;一个输入数据缓冲区中的偏移量,以及几个我们在交易中需要获取的确切字段的参数。详情请参阅我们的 RFC:https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0009-vm-syscalls/0009-vm-syscalls.md为了保证最大的灵活性,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 bashroot@dc2c0c209dcd:/# cd /coderoot@dc2c0c209dcd:/code# riscv64-unknown-elf-gcc -Os carrot.c -o carrotroot@dc2c0c209dcd:/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: Vec
可以看到,我没有直接将脚本代码嵌入到脚本数据结构中,而是只包含了代码的哈希,这是实际脚本二进制代码的 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)
现在我们准备签名并fā送交易:
[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 脚本的脚本,我们需要做的是:
将脚本编译为 RISC-V 可执行的二进制文件;在 Cell 的 Data 部分部署二进制文件;创建一个 Type 脚本数据结构,使用二进制文件的 Blake2b 散列作为 code hash,补齐 args 部分中脚本代码的需要的参数;用生成的 Cell 中设置的 Type 脚本创建一个新的交易;将包含脚本代码的 Cell 的 Outpoint 写入到一笔交易的 Deps 中去。
zhè就是你所yǒu需要的!如果您的脚běn遇到任何问题,您需要检查这些要点。
虽然在这里我们只讨论了 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 shù据作为当前输入
对于默认 Lock 脚本,假设 Witness 中的第一个参数包含由 Cell 所有者签名的可恢复签名,其余参数是用户提供的可选参数
默认的 Lock 脚本运行由交易 Hash 链接的二进制程序的 Blake2b Hash, 还有所有用户提供的参数(如果存在的话)
将 Blake2b Hash 结果用作 Secp256k1 签名验证的消息部分。注意,Witness 数据结构中的第一gè参数提供了实际的签名。
如果签名验证失败,脚本退出并返回错误码。否则它将继续下一个迭代。
注意,我们在前面讨论了脚本和脚本代码之间的区别。每一个不同的公钥哈希都会产生不同的 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 数据jié构的 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;}
我们再来看xià胡萝卜这个例子,我们检查是否有更多的输入 Cell 要测试。与之前的例子有两个不同:
如果我们只想知道一个 Cell 是否存在并且不需要任何数据,我们只需要传入 NULL 作为数据缓冲区,一个 len 变量的值是 0。通过这种方式,Syscall 将跳过数据填充,只提供可用的数据长度和正确的返回码用于处理。
在这个胡萝卜的例子中,我们循环遍历交易中的所yǒu输入,但这里我们只关心具有相同 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 (len > WITNESS_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; 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 上使yòng JavaScript,我们也可以通过 Mruby 在 CKB 上使用 Ruby, 我们甚至可以将比特币脚本或 EVM 放到链上,我们只需要编译他们的虚拟机,并把它放在链上。这确保了 CKB-VM 既能bāng助我们保存资产,又能构建一个多样化的生态系统。所有的语言都应该在 CKB 上被平等对待,自由应该掌握在区块链合约的开发者手中。
在这个阶段,你可能觉得:是的,这是可能的,但是 VM 之上的 VM 不会很慢吗?我相信这取决于你的例子是否很慢。我坚信,基准测试没有任何意义,除非我们将它放在具有标准硬件需求的实际用例中。所以我们需yào让时间来检验这是否真的会成为一个问题。在我看来,高级语言更可能用于 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 bashroot@0d31cad7a539:~# cd /coderoot@0d31cad7a539:/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,-sroot@0d31cad7a539:/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!\\\\\”)\”)])
注意,使用不同的cān数,你可以为不同的用例创建不同的 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;\\var 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 优化比赛,这只是一个思考。
下集预告
我知道这是一个很长的帖子,我希望你已经尝试过并成功地部署了一gè脚本到 CKB。在下一篇文章中,我们将介绍一个重要的主题:如何在 CKB 中让自己的用户定义 Token(UDT)。CKB 上 UDT 最好的部分是,每个用户都可以将自己的 UDT 存储在自己的 Cell 中,这与 Ethereum 上的 ERC20 令牌不同,在 Ethereum 上,每个人的 Token 都必须位于 Token 发起者的单个地址中。所有这些都可以通过单独使用 Type Script 来实现。
如果你感兴趣,欢迎继续关注!
火星文转换的火星文输入法?
火星文输入法是2007年10月由“我爱火星文工作室“推出的一款火星文即时转换软件。无论你用拼音还是五笔输入法打字,火星文输入法都能把文字自动转换为酷酷的火星文,不必改变原有的打字习惯。比如“冇怪嘼,请吥崾靠近↖(^ω^)↗”、“Sòrγy,涐lōve伱”、“瀙嗳の”等等,让文字输入新鲜有趣。
除此之外,还能随时切换火星文、繁体、简体三种状态,并随意打出像*^_^*、(⊙o⊙)、(^_^)/~~这样的个性火星fú号,同时提供强大的DIY功能。
火星文输入法V2.4版本,增加了由用户自由控制火星文出现频率的百分比,并提供标准版、超级火星版、随机版三种火星等级。
火星文输入法V2.9.0版新增了“菊花文”转换功能。
以上就是关于火星文带符号转换器,火星文字转换器的知识,后面我们会继续为大家整理关于火星文符号转换器的知识,希望能够帮助到大家!
联系我们
关注公众号