原文做者:Xuejie
原文連接:Introduction to CKB Script Programming 2: Script
本文譯者:Shooter,Jason,Orange (排名不分前後)html
上一篇咱們介紹了當前 CKB 的驗證模型。這一篇會更加有趣一點,咱們要向你們展現如何將腳本代碼真正部署到 CKB 網絡上去。我但願在你看完本文後,你能夠有能力自行去探索 CKB 的世界並按照你本身的意願去編寫新的腳本代碼。git
須要注意的是,儘管我相信目前的 CKB 的編程模型已經相對穩定了,可是開發仍在進行中,所以將來還可能會有一些變化。我將盡力確保本文始終處於最新的狀態,可是若是在過程到任何疑惑,本文以 此版本下的 CKB 做爲依據。程序員
警告:這是一篇很長的文章,由於我想爲下週更有趣的話題提供充足的內容。因此若是你沒有充足的時間,你沒必要立刻完成它。我在試着把它分紅幾個獨立的不凡,這樣你就能夠一次嘗試一個。
github
在繼續以前,咱們先來區分兩個術語:腳本(script)和腳本代碼(script code)docker
在本文以及整個系列文章內,咱們將區分腳本和腳本代碼。腳本代碼其實是指你編寫和編譯並在 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。json
請注意,在大多數狀況下,腳本和腳本代碼是能夠互換使用的,可是若是你在某些地方感到了困惑,那麼你可能有必要考慮一下二者間的區別。
ubuntu
你可能以前就已經聽所過了,CKB (編者注:此處指的應該是 CKB VM)是基於開源的 RISC-V 指令集編寫的。但這到底意味着什麼呢?用我本身的話來講,這意味着咱們(在某種程度上)在 CKB 中嵌入了一臺真正的微型計算機,而不是一臺虛擬機。一臺真正的計算機的好處是,你能夠用任何語言編寫任何你想寫的邏輯。在這裏,咱們展現的前面幾個例子將會用 C語言編寫,以保持簡單性(我是說工具鏈中的簡單性,而不是語言),以後咱們還會切換到基於 JavaScript 的腳本代碼,並但願在本系列中展現更多的語言。記住,在 CKB 上有無限的可能!api
正如咱們提到的,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 (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; }
如下幾點須要解釋一下:
len
字段須要標記爲volatile
。咱們會同時使用它做爲輸入和輸出參數,CKB VM 只能在它還保存在內存中時,才能夠把它設置輸出參數。而volatile
能夠確保 C 編譯器將它保存爲基於 RISC-V 內存的變量。syscall
時,咱們須要提供如下功能:一個緩衝區來保存syscall
提供的數據;一個len
字段,來表示系統調用返回的緩衝區長度和可用數據長度;一個輸入數據緩衝區中的偏移量,以及幾個咱們在交易中須要獲取的確切字段的參數。詳情請參閱咱們的RFC。CKB_SUCCESS
) 意味着成功,1 (or CKB_INDEX_OUT_OF_BOUND
) 意味着您已經經過一種方式獲取了全部的索引,2 (orCKB_ITEM_MISSING
) 意味着不存在一個實體,好比從一個不包含該 type 腳本的 cell 中獲取該 type 的腳本。概況一下,這個腳本將循環遍歷交易中的全部輸出 cells,加載每一個 cell data 的前6個字節,並測試這些字節是否和carrot
匹配。若是找到匹配,腳本將返回-1
,表示錯誤狀態;若是沒有找到匹配,腳本將返回0
退出,表示執行成功。
爲了執行該循環,該腳本將保存一個index
變量,在每次循環迭代中,它將試圖讓 syscall 獲取 cell 中目前採用的index
值,若是 syscall 返回 CKB_INDEX_OUT_OF_BOUND
,這意味着腳本已經遍歷全部的 cell,以後會退出循環;不然,循環將繼續,每測試 cell data 一次,index
變量就會遞增一次。
這是第一個有用的 CKB 腳本代碼!在下一節中,咱們將看到咱們是如何將其部署到 CKB 中並運行它的。
首先,咱們須要編譯上面寫的關於胡蘿蔔的源代碼。因爲 GCC 已經提供了 RISC-V 的支持,您固然可使用官方的 GCC 來建立腳本代碼。或者你也可使用咱們準備的 docker 鏡像來避免編譯 GCC 的麻煩:
$ ls carrot.c ckb_consts.h ckb_syscalls.h $ sudo docker run --rm -it -v `pwd`:/code nervos/ckb-riscv-gnu-toolchain:xenial bash root@dc2c0c209dcd:/# cd /code root@dc2c0c209dcd:/code# riscv64-unknown-elf-gcc -Os carrot.c -o carrot root@dc2c0c209dcd:/code# exit exit $ ls carrot* carrot.c ckb_consts.h ckb_syscalls.h
就是這樣,CKB 能夠直接使用 GCC 編譯的可執行文件做爲鏈上的腳本,無需進一步處理。咱們如今能夠在鏈上部署它了。注意,我將使用 CKB 的 Ruby SDK,由於我曾經是一名 Ruby 程序員,固然 Ruby 對我來講是最天然的(但不必定是最好的)。如何設置請參考官方 Readme 文件。
要將腳本部署到 CKB,咱們只需建立一個新的 cell,把腳本代碼設爲 cell data 部分:
pry(main)> data = File.read("carrot") pry(main)> data.bytesize => 6864 pry(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<Bytes>, 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.clear pry(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 腳本的腳本,咱們須要作的是:
code hash
,補齊args
部分中腳本代碼的須要的參數這就是你全部須要的!若是您的腳本遇到任何問題,您須要檢查這些要點。
雖然在這裏咱們只討論了 type 腳本,可是 lock 腳本的工做方式徹底相同。您唯一須要記住的是,當您使用特定的 lock 腳本建立 cell 時,lock 腳本不會在這裏運行,它只在您使用 cell 時運行。所以, type 腳本能夠用於構造建立 cell 時運行的邏輯,而 lock 腳本用於構造銷燬 cell 時運行的邏輯。考慮到這一點,請確保您的 lock 腳本是正確的,不然您可能會在如下場景中丟失 token:
您的 lock 腳本有一個其餘人也能夠解鎖您的 cell 的 bug。
您的 lock 腳本有一個 bug,任何人(包括您)都沒法解鎖您的 cell。
在這裏咱們能夠提供的一個技巧是,始終將您的腳本做爲一個 type 腳本附加到你交易的一個 output cell 中去進行測試,這樣,發生錯誤時,您能夠當即知道,而且您的 token 能夠始終保持安全。
根據已經掌握的知識,讓咱們看看 CKB 中包含的默認的 lock 腳本代碼。 爲了不混淆,咱們正在查看 lock 腳本代碼在 這個commit。
默認的 lock 腳本代碼將循環遍歷與自身具備相同 lock 腳本的全部的 input cell,並執行如下步驟:
注意,咱們在前面討論了腳本和腳本代碼之間的區別。每個不一樣的公鑰 hash 都會產生不一樣的 lock 腳本,所以,若是一個交易的輸入 cell 具備相同的默認 lock 腳本代碼,但具備不一樣的公鑰 hash(所以具備不一樣的 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 要測試。與以前的例子有兩個不一樣:
NULL
做爲數據緩衝區,一個 len
變量的值是 0。經過這種方式,syscall 將跳過數據填充,只提供可用的數據長度和正確的返回碼用於處理。
cell
命名爲group
。 咱們可使用 CKB_SOURCE_GROUP_INPUT
代替 CKB_SOURCE_INPUT
, 來表示只計算同一組中的 cell,舉個例子,即具備與當前 cell 相同的 lock 腳本的 cells。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的文檔是你最好的朋友。
/* 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 < ns(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 腳本中使用的方案與如今比特幣中使用的方案很是類似。
我相信你和我如今的感受同樣: 咱們能夠用 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,使其愈來愈快,:P
要在 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 root@0d31cad7a539:~# cd /code root@0d31cad7a539:/code# make riscv64-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.o riscv64-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.o riscv64-unknown-elf-gcc build/entry.o build/duktape.o -o build/duktape -lm -Wl,-static -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,-s root@0d31cad7a539:/code# exit exit $ ls build/duktape build/duktape*
與 carrot 示例同樣,這裏的第一步是在 CKB cell 中部署 duktape 腳本代碼:
pry(main)> data = File.read("../ckb-duktape/build/duktape") pry(main)> duktape_data.bytesize => 269064 pry(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))
與 carrot 的例子不一樣,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.clear pry(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 來實現。
若是你感興趣,請繼續關注 :)
Nervos Community 致力於成爲最好的 Nervos 社區,咱們將持續地推廣和普 及 Nervos 技術,深刻挖掘 Nervos 的內在價值,開拓 Nervos 的無限可能, 爲每一位想要深刻了解 Nervos Network 的人提供一個優質的平臺。
添加微信號:BitcoinDog 便可加入 Nervos Community,若是是程序員請備註,還會將您拉入開發者羣。