CKB脚本编程简介[2]:脚本基础
原文作者:Xuejie原文链接:Introduction to CKB Script Programming 2: Script本文译者:Shooter,Jason,Orange (排名不分先后)
上一篇我们介绍了当前 CKB 的验证模型。这一篇会更加有趣一点,我们要向大家展示如何将脚本代码真正部署到 CKB 网络上去。我希望在你看完本文后,你可以有能力自行去探索 CKB 的世界并按照你自己的意愿去编写新的脚本代码。
需要注意的是,尽管我相信目前的 CKB 的编程模型已经相对稳定了,但是开发仍在进行中,因此未来还可能会有一些变化。我将尽力确保本文始终处于最新的状态,但是如果在过程到任何疑惑,本文以 此版本下的 CKB 作为依据。
警告:这是一篇很长的文章,因为我想为下周更有趣的话题提供充足的内容。所以如果你没有充足的时间,你不必马上完成它。我在试着把它分成几个独立的不凡,这样你就可以一次尝试一个。
语法
在继续之前,我们先来区分两个术语:脚本(script)和脚本代码(script code)
在本文以及整个系列文章内,我们将区分脚本和脚本代码。脚本代码实际上是指你编写和编译并在 CKB 上运行的程序。而脚本,实际上是指 CKB 中使用的脚本数据结构,它会比脚本代码稍微多一点点:
pub struct Script {
pub args: Vec<Bytes>,
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 (编者注:此处指的应该是 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
封装在了一个易于使用的头文件中,非常欢迎您在这里查看这个文件,了解如何实现syscalls
。最重要的是,您可以只获取这个头文件并使用包装函数来创建您想要的系统调用。
现在有了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