break;
pry(main)> udt_out_point = CKB::Types::OutPoint.new(tx_hash: root_udt_tx_hash, index: 0)
diff --git a/udt.js b/udt.js
+ throw "Cannot fetch the first input";
new Uint8Array(first_input),
首先,让我们建设一个包括 1000000 Token 的 UDT:
+ if (!((input_index === 0) && (output_index === 1))) {
+ throw "Requires only one argument!";
pry(main)> tx.outputs.push(tx.outputs[1].dup)
+ input_index += 1;
@@ -33,5 +37,17 @@ while (true) {
+ var view = new DataView(buffer);
pry(main)> tx.outputs_data.push(CKB::Utils.bin_to_hex([1000000].pack("L<")))
虽然,我们也可以结构一个非凡的 Cell 来生存所有 UDT 用户的余额。这个办理方案看起来很像以太坊的 ERC20 设计。可是这中间存在几个问题:
此刻我们可以实验将 UDT 转移到另一个帐户。首先让我们实验建设一个输出 UDT 比输入 UDT 更多的 UDT 生意业务:
同样地,我们可以获取输出的UDT的总和并举办较量:
index e02b993..cd443bf 100644
throw "Invalid token issuing mode!";
这种简朴而强大的模式确保了 UDT 的安详,同时也带来了可以在差异 Cell 之间自由生意业务的长处。据我们所知,这种模式在其他许多声称机动或可编程的区块链中是不行能实现的。
+ if (ret === CKB.CODE.INDEX_OUT_OF_BOUND) {
· 这里,我们严格确保输出 UDT 的总和便是输入 UDT 的总和,但在某些环境下,仅仅确保输出 UDT 的总和不高出输入 UDT的总和就足够了。换句话说,当不需要时,用户可以选择为空间烧毁一部门 UDT;
pry(main)> tx.witnesses.push(CKB::Types::Witness.new(data: []))
以上险些就是验证第一条法则所需的全部内容:输出 Cell 中 UDT 的总和应便是输入 Cell 中 UDT 的总和。换句话说,利用这种 Type Script,便是没有人可以或许伪造任何新的 Token。这不是很棒吗?
index 4a20bd0..e02b993 100644
}
if (ret !== 4) {
+ if (ret !== 4) {
pry(main)> tx.outputs_data[0] = CKB::Utils.bin_to_hex([1000000].pack("L<"))
var first_input = CKB.load_input(0, 0, CKB.SOURCE.INPUT);
+}
}
deleted file mode 100644
@@ -1,3 +1,7 @@
pry(main)> arg = CKB::Utils.bin_to_hex(CKB::Serializers::InputSerializer.new(tx.inputs[0]).serialize)
+ break;
数据模子
+if (input_coins !== output_coins) {
pry(main)> tx.outputs_data[2] = CKB::Utils.bin_to_hex([100000].pack("L<"))
output_index += 1;
+}
+ output_index += 1;
}
}
pry(main)> tx.outputs_data[0] = CKB::Utils.bin_to_hex([900000].pack("L<"))
var view = new DataView(buffer);
+ ret = CKB.raw_load_cell_data(buffer, 0, input_index, CKB.SOURCE.GROUP_INPUT);
+ ret = CKB.raw_load_cell_data(buffer, 0, output_index, CKB.SOURCE.GROUP_OUTPUT);
· 在这里,我们将剧本限制为仅在初始 Token 建设进程中建设一个 Cell ,实际上也可以建设多个 Cell , 别离用于差异的用途;
var ret = CKB.CODE.INDEX_OUT_OF_BOUND;
pry(main)> tx = wallet.generate_tx(wallet2.address, CKB::Utils.byte_to_shannon(20000))
我不像某些项目,只知道扔出来一个视频可能很是气人的帖子,也不说清楚是怎么做的可能怎么办理问题的。假如没有实际的代码和利用它的步调,那我以为这个帖子其实是很没意思的。以下是如安在 CKB 上利用上述 UDT 剧本:
· 思量到 CKB 中 Cell 的更新实际上是在销毁旧 Cell 并从头生成新的 Cell ,因今生存所有余额的单个 Cell 会碰着一个逆境:一旦更新 UDT 余额那就不得不更新这一个且是独一的 Cell,并且每一步的操纵都需要更新, 那么用户在利用进程中将会发生斗嘴。
pry(main)> duktape_data_hash = CKB::Blake2b.hexdigest(duktape_data)
· ckb-duktape: 55849c20b43a212120e0df7ad5d64b2c70ea51ac
无论我们如何实验,我们都无法建设另一个想要伪造沟通 UDT Token 的 Cell。
output_coins += view.getUint32(0, true);
}
陈设到 CKB 网络
pry(main)> tx.outputs[2].type = duktape_udt_script
pry(main)> duktape_tx_hash = wallet.send_capacity(wallet.address, CKB::Utils.byte_to_shannon(300000), CKB::Utils.bin_to_hex(duktape_data))
throw "Cannot fetch the first input";
一个非凡的 Type Script 暗示此 Cell 存储 UDT;
+ function(x) { return ('00' + x.toString(16)).slice(-2); }).join('');
1. 假如进攻者试图利用沟通的参数,则 Script 将验证出生意业务中的第一个输入 OutPoint 与参数不匹配,从而使生意业务无效;
input_coins += view.getUint32(0);
}
但这里尚有一个问题:假如 Token 存储在属于各个用户的浩瀚差异 Cell 中,,而不是统一存储,我们如何确定这个 Token 确实由这个刊行者刊行呢?假如有人本身伪造 Token 怎么办?在以太坊中,这大概是一个问题,但正如我们将在本文中看到的,CKB 中的 Type Script 可以防备所有这些进攻,确保 Token 是安详的。
正如前一篇文章中所表明的,CKB 要求我们利用轮回来迭代同一 group 中所有的 Input 并获取数据。在 C 中我们将利用 ckb_load_cell_data,它被包装到 JS 函数 ckb.raw_load_cell_data 中。正如 ArrayBuffer 所示,我们只对 Cell 数据的前 4 个字节感乐趣,因为这 4 个字节将包括 UDT 的数量。
@@ -0,0 +1,17 @@
throw "Invalid output cell!";
var buffer = new ArrayBuffer(4);
--- a/udt.js
if (ret === CKB.CODE.INDEX_OUT_OF_BOUND) {
throw "Invalid input cell!";
if (CKB.ARGV.length !== 1) {
2. 假如进攻者试图利用沟通的参数并填充参数作为第一个输入 OutPoint,它将造成一个双花错误,也会使生意业务无效;
pry(main)> tx.outputs_data[0] = CKB::Utils.bin_to_hex([1000000].pack("L<"))
var buffer = new ArrayBuffer(4);
pry(main)> duktape_out_point = CKB::Types::CellDep.new(out_point: CKB::Types::OutPoint.new(tx_hash: duktape_tx_hash, index: 0))
break;
原文链接:
3. 假如进攻者试图利用差异的参数,CKB 将识别出差异的参数导致差异的 Type Script,从而生成差异的UDT。
· 上述 UDT 剧本不答允在初始建设完成后再刊行更多的 Token,但大概存在另一种范例的 UDT,答允 Token 刊行者继承增发。这虽然也可以在 CKB 上运行,可是我想把这个办理方案的摸索任务留给各人,当做操练;
+ }
if (ret !== 4) {
index e69de29..0000000
此刻我们实验发送 1000000 UDT 给另一个用户,同时为发送者本人保存 1000000 UDT,很明明这会触发错误,因为我们正在实验伪造更多的 Token。可是假如稍作修改,我们可以看到,假如您遵守总和验证法则,UDT 转移生意业务是有效的:
Cell 数据的前 4 个字节包括当前 Cell 中的 UDT 数量。
+ }
index e69de29..4a20bd0 100644
+var input_index = 0;
pry(main)> duktape_udt_script = CKB::Types::Script.new(code_hash: duktape_data_hash, args: [CKB::Utils.bin_to_hex(File.read("udt.js")), arg])
· 在一个 UDT 转账生意业务中,输出 Cell 中的 UDT 总和应便是输入 Cell 中 UDT 的总和;
CKB::RPCError: jsonrpc error: {:code=>-3, :message=>"UnresolvableTransaction(Dead(OutPoint(0x0b607e9599f23a8140d428bd24880e5079de1f0ee931618b2f84decf2600383601000000)))"}
Xuejie 是 CKB-VM 的焦点开拓者,他在本身的博客「Less is More」中,创作了一系列先容 CKB 剧本编程的文章,用于增补白皮书中编写 CKB 剧本所需的所有缺失的细节实现。本文是该系列的第三篇,具体先容了机动又好玩的自界说代币,快来一起玩耍吧!
var hex_input = Array.prototype.map.call(
pry(main)> tx.outputs[0].type = duktape_udt_script
diff --git a/udt.js b/udt.js
}
+ }
var output_coins = 0;
pry(main)> root_udt_tx_hash = api.send_transaction(signed_tx)
+ break;
+++ b/udt.js
这里尚有没有 Diff 名目标完整 UDT 剧本,有需自取:
+ input_coins += view.getUint32(0, true);
+ if (ret !== 4) {
为简朴起见,我们这里将在纯 JavaScript 中编写 UDT 剧本,虽说 C 版本大概有助于节减 Cycles ,但成果其实是一样的。
+++ b/udt.js
CKB 的 Cell 模子和 VM 支持很多新的用例。然而,这并不料味着我们需要丢弃现有的一切。如今区块链中的一个常见用途是 Token 刊行者宣布具有非凡目标/意义的新 Token。在以太坊中,我们称之为 ERC20 Token,下面让我们看看我们如安在 CKB 中构建雷同的观念。为了与 ERC20 区分,在 CKB中的 Token 我们称之为 user defined token,简称 UDT。
var input_coins = 0;
pry(main)> tx = wallet.generate_tx(wallet.address, CKB::Utils.byte_to_shannon(20000))
+}
+ throw "Invalid creation argument!";
var input_index = 0;
假如我们再次实验提交沟通的生意业务,双花将阻止我们伪造沟通的 Token :
if (typeof first_input === "number") {
function(x) { return ('00' + x.toString(16)).slice(-2); }).join('');
+while (true) {
diff --git a/udt.js b/udt.js
3. 不然以失败状态退出。
}
while (true) {
input_coins += view.getUint32(0, true);
var input_index = 0;
+ if (CKB.ARGV[0] != hex_input) {
+ }
+}
我们此刻有谜底了!CKB 中最小 UDT 的 Type Script 完整验证流程如下:
+var buffer = new ArrayBuffer(4);
+
· 实际上,大概有很多 Cell 包括沟通的 UDT;
while (true) {
但有一个问题:当我们说没有人可以或许伪造新的 Token 时候,我们说的真的是指没有任何人,包罗代币刊行人!那就不太好了,所以我们需要添加一个破例,让 Token 刊行者可以先刊行 Token,但之后就没有人可以或许这么做了。那有没有步伐做到这一点?
$ cat udt.js
+ output_coins += view.getUint32(0, true);
这引出了我们提出的设计:
Xuejie 在本身的博客中,已经更新了该系列的第四篇「WebAssembly on CKB」以及第五篇「Debugging」,别的,还开启了全新的系列:「一起成立一个最小区块链」,接待各人点击阅读原文或该链接:https://xuejie.space/,和 Xuejie 一起摸索!
+ }
+var input_coins = 0;
请留意,这里只是一些例子,CKB 剧本的实际应用方法是没有界线的。我们很是兴奋看到未来有更多的 CKB dApp 开拓者缔造出让我们震惊的有趣用法。
+ }
这大概听起来有点傲慢,但我们会证明,通过 Type Script 和 CKB 奇特的设计模式,一切都可以搞定:P
throw "Requires only one argument!";
固然有一些办理方案确实可以缓解甚至能办理上述问题,但我们照旧开始质疑这里的根基设计:将所有 UDT 生存在一个处所真的有意义吗?一旦转账,UDT 应该真的属于收款人,为什么余额仍然留在一其中心呢?
}
为了能在 CKB 上运行 JavaScript,让我们首先在 CKB 上陈设 duktape:
+
ret = CKB.raw_load_cell_data(buffer, 0, output_index, CKB.SOURCE.GROUP_OUTPUT);
此刻想想这个问题:什么对象不能被包括在区块链中两次?生意业务输入中的 OutPoint!第一次的时候,我们将 OutPoint 作为生意业务输入包括在内,引用的 Cell 将被耗损,假如有人稍后再次包括它,则会发生双花错误,这正是我们利用区块链的原因。
· 固然我们只在这里先容 ERC20,但 ERC721 也应该是完全大概的。
pry(main)> tx.cell_deps.push(duktape_out_point.dup)
本文利用 CKB v0.20.0 版原来演示。详细来说,我会在每个项目中利用以下提交的版本:
pry(main)> tx.cell_deps.push(duktape_out_point.dup)
这种设计有几个寄义:
pry(main)> api.send_transaction(signed_tx)
}
这就是我们所说的 CKB 奇特的设计模式:通过利用输入 OutPoint 作为 Script 参数,我们可以建设一个无法再伪造的奇特 Script:
+ var view = new DataView(buffer);
--- a/udt.js
+
+ new Uint8Array(first_input),
+
此刻我们终于可以完成我们的 UDT 剧本了:
虽然有!但谜底就像一个谜语,所以请仔细阅读本段:Type Script 由两部门构成:暗示实际代码的代码哈希,以及 Type Script 利用的参数。具有差异参数的两种 Type Script 将被视为两种差异 Type Script。这里的能力是答允 Token 刊行者建设一个具有新 Type Script 的 Cell,但没有人可以或许再次建设,所以假如我们在参数部门安排一些不能再包括的对象,那么问题就被办理了~
+while (true) {
译者:Joey
+ if (typeof first_input === "number") {
pry(main)> data = File.read("../ckb-duktape/build/duktape")
--- a/udt.js
pry(main)> tx.witnesses[0].data.clear
· 用户可以将 Cell 中的全部或部门 UDT 转账给其他人;
鉴于上述设计,最小 UDT Type Script 应该遵循以下法则:
假如你此刻还跟得上我,那么必然可以看出:步调 1 对应于正常的 UDT 生意业务,而步调 2 对应于初始 Token 建设进程。
throw "Invalid creation argument!";
input_index += 1;
· ckb-sdk-ruby: 1c2a3c3f925e47e421f9e3c07164ececf3b6b9f6
}
· Token 的刊行者必需提供足够的存储空间以生存所有用户的余额。跟着用户数量的增长,存储空间也将增长,这在 CKB 的经济模子中,不是一个高效的设计。
pry(main)> api.send_transaction(signed_tx)
+var output_coins = 0;
pry(main)> signed_tx = tx.sign(wallet.key, api.compute_transaction_hash(tx))
+if (CKB.ARGV.length !== 1) {
https://xuejie.space/2019_09_06_introduction_to_ck...
if (input_coins !== output_coins) {
+ var first_input = CKB.load_input(0, 0, CKB.SOURCE.INPUT);
if (CKB.ARGV[0] != hex_input) {
机动的法则
每个 Token 用户将其 UDT 生存在本身的 Cell 中。他们认真为 UDT 提供存储空间,并确保他们本身的 Token 是安详的。这样 UDT 就可以真正属于每个 UDT 用户。
}
+++ b/udt.js
}
· ckb: 472252ac5333b2b19ea3ec50d54e68b627bf6ac5
pry(main)> tx.inputs.push(CKB::Types::Input.new(previous_output: udt_out_point, since: "0"))
+
作者:Xuejie
就是上面这样,一共有 53 行代码 1372 字节,我们就在 CKB 中完成了一个最小的 UDT Type Script。留意,在这里我甚至还没有利用压缩东西,假如利用任何一个符合的 JS 压缩东西,我们应该可以或许得到更紧凑的 Type Script 。虽然了,这是一个可用于出产情况的 Type Script ,但它足以显示一个简朴的 Type Script 可以处理惩罚 CKB 中的许多重要任务。
· UDT Cell 的存储本钱始终是恒定的,与存储在 Cell 中的 UDT 数量无关;
编写 UDT 剧本
+var output_index = 0;
pry(main)> tx.witnesses[0].data.clear
var output_index = 0;
CKB::RPCError: jsonrpc error: {:code=>-3, :message=>"InvalidTx(ScriptFailure(ValidationFailure(-2)))"}
· 用于掩护 UDT 的 Lock Script 与 UDT 自己疏散。
- throw "Input coins do not equal output coins!";
+ throw "Invalid output cell!";
+ }
pry(main)> tx.outputs[2].capacity = CKB::Utils::byte_to_shannon(20000)
2. 查抄 Type Script 的第一个参数是否与当前生意业务中的第一个 OutPoint 匹配,假如它们匹配,则以乐成状态退出;
diff --git a/contract.js b/contract.js
pry(main)> signed_tx = tx.sign(wallet.key, api.compute_transaction_hash(tx))
if (ret === CKB.CODE.INDEX_OUT_OF_BOUND) {
+ var hex_input = Array.prototype.map.call(
首先,我们需要遍历所有输入 Cell 并收集 UDT 的总和:
var view = new DataView(buffer);
+ throw "Invalid token issuing mode!";
1. 首先收集输入 Cell 中所有 UDT 的总和以及输出 Cell 中所有 UDT 的总和,假如它们相等,则 Type Script 将以乐成状态退出;
pry(main)> tx.outputs[0].type = duktape_udt_script
pry(main)> api.send_transaction(signed_tx)
input_index += 1;
· 只有刊行者可以在初始Token建设进程中生成新的 Token。
+ throw "Input coins do not equal output coins!";
以太坊会为每个合约账户提供单独的存储空间,CKB 与之差异,CKB 是在多个 Cell 之间通报数据。Cell 的 Lock Sript 和 Type Sript 会标明 Cell 属于哪个帐户,以及如何与 Cell 举办交互。其功效是,CKB 不会像 ERC20 那样将所有 Token 用户的余额存储在 ERC20 合约的存储空间中,在 CKB 中,我们需要一种新的设计来存储 UDT 用户的余额。
留意,这里我们对 input_coins 执行了一个简朴的 Add 操纵,这风险很高。之所以这样做只是为了简朴起见。在真实的出产情况中,您应该查抄该值是否保持在 32 位整数值中。假如有须要,应利用更高精度的数字范例。
var input_coins = 0;
@@ -15,3 +15,23 @@ while (true) {
if (!((input_index === 0) && (output_index === 1))) {
ret = CKB.raw_load_cell_data(buffer, 0, input_index, CKB.SOURCE.GROUP_INPUT);
+ if (ret === CKB.CODE.INDEX_OUT_OF_BOUND) {
pry(main)> signed_tx = tx.sign(wallet.key, api.compute_transaction_hash(tx))
}
这里显示的 UDT 剧本仅作为示例,实际上,dApp 大概更巨大而且需要更多成果。您还可以按照需要为 UDT 剧本添加更多成果,个中一些示例包罗:
if (input_coins !== output_coins) {
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。