摘要:若返回值為表示合約調用成功,返回值為其它表示合約調用失敗。舉個例子,我們可以將編譯為二進制文件,來啟用基于的合約開發。共識規則強制規定交易中所有的容量總和不能超過所有的容量總和。
</>復制代碼
Nervos 底層公鏈 CKB 的虛擬機(CKB-VM)是基于 RISC-V 打造的區塊鏈虛擬機。在前三節課中,我們介紹了 CKB 虛擬機的設計理念及優勢。那么,怎樣才能利用 CKB-VM 更好的開發呢?本文是實現 CKB 背后腳手架-技術系列的最后一篇文章,CKB-VM 設計者肖雪潔會以三種不同的方式展示 CKB-VM 的合約示例,它會幫助你更好的在 CKB-VM 上玩耍~
秘猿科技區塊鏈小課堂第 24 期
以下代碼示例為可以在 CKB-VM 上運行的最簡化智能合約:
</>復制代碼
int main()
{
return 0;
}
以下代碼可以通過 GCC 編譯:
</>復制代碼
riscv64-unknown-elf-gcc main.c -o main
CKB 的智能合約是一個遵循傳統 Unix 調用方式的二進制文件。可以通過 argc/argv 輸入參數,以 main 函數的返回值來表示輸出結果。
若返回值為 0 表示合約調用成功,返回值為其它表示合約調用失敗。
為了簡化說明,我們以 C 語言為例來實現示例中的合約。但實際上任何可以編譯成 RISC-V 指令集的語言均可以直接用來開發 CKB 的智能合約:
最新版的 Rust Stable 已經有 RISC-V
Go 語言的 RISC-V 支持也在開發中:
對于更高級的語言,我們可以直接將其 C 語言的實現編譯為 RISC-V 二進制文件,并通過 「VM 中的 VM」 技術,在 CKB 上啟用以這些語言編寫的智能合約。舉個例子,我們可以將 mruby 編譯為 RISC-V 二進制文件,來啟用基于 Ruby 的合約開發。基于 MicroPython 的 Python 語言或基于 Duktape 的 JavaScript 語言也可以使用同樣的方式,在 CKB 上開發智能合約。
即使是編譯為 EVM 字節碼或 Bitcoin 腳本的智能合約也可以編譯為 CKB-VM 字節碼。當然我們可以清晰地看到這些傳統合約遷移到更有效字節碼上的優勢,并且,與使用較低級編程語言實現的智能合約相比,在 CKB 上的這些合約可能具有更大的運行開銷(CPU cycles),但是對于一些不同的應用場景來說,這里節省下來的開發時間以及安全性優勢,可能比在運行開銷更有價值。
CKB 還滿足了哪些需求?除了最簡化的合約之外,CKB 也會提供一個系統庫來滿足如下需求:
支持 libc 核心庫;
支持動態鏈接,以減少當前合約占用的空間,比如可以通過動態鏈接在 VM 中加載 system Cell 的方式來調用庫;
讀取交易數據后,CKB-VM 中會有類似比特幣的 SIGHASH 功能,以最大限度地提高合約的靈活性。
下圖顯示了基于前面系統庫的 CKB 智能合約驗證模型:
如上圖所示,CKB 的交易由 Input 和 Output 構成。雖然交易也可能包含 Deps(包含運行合約時所需的數據或代碼的依賴項),但它們僅會影響智能合約的實現,并且會從交易模型中刪除。
交易的每個 Input 都會引用一個現有 Cell,一筆交易可以覆蓋、銷毀或生成一個 Cell。共識規則強制規定交易中所有 Output Cell 的容量總和不能超過所有 Input Cell 的容量總和。
驗證智能合約的標準CKB-VM 使用以下標準來驗證智能合約:
每個 Input 中都會包含一個解鎖腳本(Unlock Script),用于驗證交易發起者是否可以使用當前 Input 所引用到的 Cell。Unlock Script 中包含由交易發起者生成的簽名,且 Unlock Script 通常會有指定的簽名算法(如 SIGHASH-ALL-SHA3-SECP256K1)。CKB 會通過 VM 運行 Unlock Script 進行驗證:智能合約會通過一個 API 來讀取交易數據以實現 SIGHASH 相關的計算,從而提供最大的靈活性。
每個 Cell 中包含一個驗證腳本,用于驗證當前 Cell Data 是否滿足先前指定的條件,例如我們可以創建一個 Cell 來保存用戶自定義代幣(User Defined Token,簡稱 UDT)。在 Cell 創建完畢后,我們需要驗證 Input Cell 中所有 Token 總和是否大于或等于 Output Cell 中所有 Token 的總和,以確保交易中不會生成新的 Token。為增強安全性,CKB 的合約開發人員還可以利用特殊合約來確保創建 Cell 后,Cell 的驗證腳本不會被修改。
Input Cell 驗證示例基于上述對 CKB-VM 安全模型的描述,我們可以首先實現一個完整的 SIGHASH-ALL-SHA3-SECP256K1 合約,來驗證所提供的簽名以及驗證提供簽名的交易發起者是否可以使用當前的 Cell:
</>復制代碼
// For simplicity, we are keeping pubkey in the contract, however this
// solution has a potential problem: even though many contracts might share
// the very same structure, keeping pubkey here will make each contract
// quite different, preventing common contract sharing. In CKB we will
// provide ways to share common contract while still enabling each user
// to embed their own pubkey.
char* PUBKEY = "this is a pubkey";
int main(int argc, char* argv[])
{
// We need 2 arguments for this contract
// * The first argument is contract name, this is for compatibility issue
// * The second argument is signature for current contract input
if (argc < 2) {
return -1;
}
// This function loads current transaction into VM memory, and returns the
// pointer to serialized transaction data. Notice ckb_mmap might preprocess
// the transaction a bit, such as removing signatures in all tx inputs to
// avoid chicken-egg problem when signing signature.
int length = 0;
char* tx = ckb_mmap(CKB_TX, &length);
if (tx == NULL) {
return -2;
}
// This function dynamically links sha3 library to current VM memory space
void *sha3_handle = ckb_dlopen("sha3");
void (*sha3_func)(const char*, int, char*) = ckb_dlsym("sha3_256");
// Here we run sha3 on all the transaction data, simulating a SIGHASH_ALL process,
// a different contract might choose to deserialize and only hash certain part
// of the transaction
char hash[32];
sha3_func(tx, length, hash);
// Now we load secp256k1 module.
void *secp_handle = ckb_dlopen("secp256k1");
int (*secp_verify_func)(const char*, int, const char*, int, const char*, int) =
ckb_dlsym("secp256k1_verify");
int result = secp_verify_func(argv[1], strlen(argv[1]),
PUBKEY, strlen(PUBKEY),
hash, 32);
if (result == 1) {
// Verification success, we are returning 0 to indicate contract succeeds
return 0;
} else {
// Verification failure
return -3;
}
}
在此示例中,我們首先將所有的交易數據讀入 CKB-VM 中以獲取交易數據的 SHA3 哈希,并將此交易數據的 SHA3 哈希、指定的公鑰和交易發起者提供的簽名提供給 secp256k1 模塊,以驗證合約中指定的公鑰是否已對提供的交易數據進行了簽名。
如果此驗證成功,則交易發起者可以使用當前 Input 引用的 Cell,合約成功執行。 如果此驗證不成功,則合約執行和交易驗證會失敗。
用戶自定義代幣(UDT)示例下面的示例中,演示了一個 Cell 驗證腳本實現類似 ERC-20 用戶自定義代幣的過程。Cell 驗證腳本也可以實現其他 UDT 功能,為簡單起見,以下示例中僅包含 UDT 的轉移功能:
</>復制代碼
int main(int argc, char* argv[]) {
size_t input_cell_length;
void* input_cell_data = ckb_mmap_cell(CKB_CELL_INPUT, 0, &input_cell_length);
size_t output_cell_length;
void* output_cell_data = ckb_mmap_cell(CKB_CELL_OUTPUT, 0, &output_cell_length);
if (input_cell_data == NULL || output_cell_data == NULL) {
return -1;
}
void* udt_handle = ckb_dlopen("udt");
data_t* (*udt_parse)(const char*, size_t) =
ckb_dlsym(udt_handle, "udt_parse");
int (*udt_transfer)(data_t *, const char*, const char*, int64_t) =
ckb_dlsym(udt_handle, "udt_transfer");
data_t* input_cell = udt_parse(input_cell_data, input_cell_length);
data_t* output_cell = udt_parse(output_cell_data, output_cell_length);
if (input_cell == NULL || output_cell == NULL) {
return -2;
}
ret = udt_transfer(input_cell, from, to, tokens);
if (ret != 0) {
return ret;
}
int (*udt_compare)(const data_t *, const data_t *);
if (udt_compare(input_cell, output_cell) != 0) {
return -1;
}
return 0;
}
在這段代碼中,首先我們通過調用系統庫讀取了 Input 與 Output Cell 中的內容,然后我們動態加載了 UDT 的實現,并使用轉移方式對 Input 進行轉換。
轉換后,Input 與 Output Cell 中的內容應該完全匹配,否則我們得到的驗證結果會是:當前交易不符合驗證腳本中指定的條件,合約執行即為失敗。
注意:以上示例僅用于展示 CKB-VM 的功能,并不代表此實現方式為 CKB 上 UDT 實現的最佳實踐。
在 Ruby 中的 Unlock Script 示例雖然上面的示例都是通過 C 語言來編寫的,但是實際上,CKB-VM 上編寫智能合約并不僅限于用 C 語言。例如我們可以將 mruby 這個針對嵌入式平臺的 Ruby 實現編譯為 RISC-V 二進制文件,并以它作為通用系統庫,這樣我們就可以使用 Ruby 在 CKB 上編寫智能合約。Unlock Script 示例如下:
</>復制代碼
if ARGV.length < 2
raise "Not enough arguments!"
end
tx = CKB.load_tx
sha3 = Sha3.new
sha3.update(tx["version"].to_s)
tx["deps"].each do |dep|
sha3.update(dep["hash"])
sha3.update(dep["index"].to_s)
end
tx["inputs"].each do |input|
sha3.update(input["hash"])
sha3.update(input["index"].to_s)
sha3.update(input["unlock"]["version"].to_s)
# First argument here is signature
input["unlock"]["arguments"].drop(1).each do |argument|
sha3.update(argument)
end
end
tx["outputs"].each do |output|
sha3.update(output["capacity"].to_s)
sha3.update(output["lock"])
end
hash = sha3.final
pubkey = ARGV[0]
signature = ARGV[1]
unless Secp256k1.verify(pubkey, signature, hash)
raise "Signature verification error!"
End
社區驅動的 CKB-VM
以上為 CKB-VM 上三種不同方式的智能合約實現示例,它們也許會幫助你更好的在 CKB-VM 上玩耍。通過 CKB-VM 的設計,我們的目標是建立一個圍繞 CKB 的社區,該社區可以自由地發展和適應新技術的進步,并且可以最大限度地減少人工干預(例如硬分叉)。 我們相信 CKB-VM 可以實現這一愿景。
注:CKB-VM 與 CKB 一樣為開源項目,目前 CKB-VM 仍在開發過程中,盡管 CKB-VM 的大部分設計已經敲定,但某些設計也可能會在將來由于你的加入而有新的進步。這篇文章是為了讓我們的社區更加了解 CKB-VM,這樣人人都可以在里面更好的玩耍并做出貢獻啦!
CKB-VM:https://github.com/nervosnetw...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/24718.html
摘要:模塊鏈的共識配置,該配置會寫入創世塊。主要指責是記錄和更新本地累計工作量最高的鏈,并維護鏈上數據的索引。消息使用序列化。協議是節點之間用來處理廣播和轉發新的交易。 by Nervos CKB Team 在 2017 年底,我們感到心里的一些想法,包括分層的網絡以及一個作為共同知識庫(Common Knowledge Base)的區塊鏈,都已經成熟。因此 2018 年元旦一過我們就迫不及...
摘要:年,包括分層的網絡以及一個作為共同知識庫的區塊鏈,都已經成熟。是一個在設計上非常不同的公有鏈協議,也是網絡中的基礎層,是整個加密經濟網絡的信任引擎。主要指責是記錄和更新本地累計工作量最高的鏈,并維護鏈上數據的索引。 說到猿起,這些心里的想法能追溯到 2016 年,甚至更早。2017 年,包括分層的網絡以及一個作為共同知識庫(Common Knowledge Base)的區塊鏈,都已經成...
摘要:目前,比特幣使用的是來進行交易簽名,并且在共識協議中使用了哈希算法。盡管的實現提供的是最流行的加密算法,但我們鼓勵社區提供更優化的加密算法實現以減少運行時開銷。 Nervos 底層公鏈 CKB 的虛擬機(CKB-VM)是基于 RISC-V 指令集打造的區塊鏈虛擬機。在上一堂分享中,我們簡單介紹了區塊鏈虛擬機,以及我們理想中的區塊鏈虛擬機的樣子。在本篇文章中,CKB-VM 設計者將詳細的...
摘要:在區塊鏈上,虛擬機就是智能合約的運行環境,是一個可以完全對外隔離的完整計算機體系。區塊鏈通過虛擬機來調用和執行智能合約,并要求所有節點都達成一致。 秘猿科技使命是用技術創造信任,為價值網絡提供基礎設施和服務。為了實現這個使命,我們三年來堅持初心,步步為營打造加密經濟網絡。我們想要讓互聯網回歸到本源,用區塊鏈技術,去構造更美好的社會,因此我們設計了 CKB 底層公鏈。我們自己造輪子,開創...
摘要:在區塊鏈上,虛擬機就是智能合約的運行環境,是一個可以完全對外隔離的完整計算機體系。區塊鏈通過虛擬機來調用和執行智能合約,并要求所有節點都達成一致。當區塊鏈遇見在很多科技領域都得到了運用,目前,也開始在區塊鏈領域逐漸的得以發展。 showImg(https://segmentfault.com/img/bVbsfi2?w=2779&h=1179); 區塊鏈的出現使得智能合約得到了更好的實...
閱讀 721·2021-11-16 11:44
閱讀 3552·2019-08-26 12:13
閱讀 3246·2019-08-26 10:46
閱讀 2362·2019-08-23 12:37
閱讀 1193·2019-08-22 18:30
閱讀 2537·2019-08-22 17:30
閱讀 1843·2019-08-22 17:26
閱讀 2296·2019-08-22 16:20