運(yùn)行節(jié)點(diǎn)
1. 接入測(cè)試網(wǎng)
請(qǐng)參考加入 ChainX 測(cè)試網(wǎng)的相關(guān)說明:
完成相關(guān)配置后,應(yīng)保證節(jié)點(diǎn)同步到最新,錢包相應(yīng)配置完成。
2. 運(yùn)行本地節(jié)點(diǎn)
請(qǐng)參考 ChainX Dev 模式的相關(guān)說明:
完成相應(yīng)配置后,請(qǐng)保證已經(jīng)出塊超過150個(gè)區(qū)塊,因?yàn)?50個(gè)塊后才會(huì)對(duì) Alice 發(fā)放第一層次獎(jiǎng)勵(lì)。
若需要反復(fù)測(cè)試,可以對(duì)超過150個(gè)塊后的區(qū)塊數(shù)據(jù)進(jìn)行備份,若需重新啟動(dòng),則只需要?jiǎng)h除老數(shù)據(jù)目錄,使用備份數(shù)據(jù)目錄替換即可。
ChainX 上的 Substrate Contracts 智能合約平臺(tái)
Substrate 作為第一個(gè)區(qū)塊鏈領(lǐng)域的技術(shù)框架,讓開發(fā)者能夠?qū)W⒂阪湹倪\(yùn)行時(shí)邏輯,而不用再花費(fèi)大量的時(shí)間精力構(gòu)建區(qū)塊鏈底層的基礎(chǔ)設(shè)施。此外, Substrate 默認(rèn)提供了很多功能模塊,比如 Staking, Consensus, 方便框架使用者根據(jù)自己的需求進(jìn)行自由組合和定制。合約模塊就是其中的一個(gè)功能模塊,不管是任何一條基于 Substrate 技術(shù)的獨(dú)立鏈,還是未來的平行鏈, 只要集成了合約模塊,就可以成為一個(gè)智能合約平臺(tái)。
本次 ChainX 智能合約平臺(tái)的主要實(shí)現(xiàn)方式就是集成 Substrate 的合約模塊,并進(jìn)行適配。ChainX 的合約功能與 Substrate 默認(rèn)的合約模塊主要區(qū)別如下:
1. 取消合約存儲(chǔ)收費(fèi)的設(shè)計(jì)。 合約存儲(chǔ)收費(fèi)簡(jiǎn)單來說就是當(dāng)合約部署到鏈上以后, 根據(jù)該合約在鏈上所占存儲(chǔ)的大小和該存儲(chǔ)的占用時(shí)間收取一定的費(fèi)用,當(dāng)合約賬戶因?yàn)橛囝~不夠無法支持存儲(chǔ)費(fèi)用時(shí),合約就會(huì)被刪除,甚至可能無法恢復(fù)。 即使合約被刪除后可以恢復(fù),目前的合約恢復(fù)可操作性也是極低,可能會(huì)對(duì)目前的合約開發(fā)造成極大的困擾。因此,我們目前決定暫時(shí)取消合約存儲(chǔ)收費(fèi),只收取合約調(diào)用的 Gas 費(fèi)用, 也就是與目前以太坊的收費(fèi)設(shè)計(jì)一致。當(dāng)合約存儲(chǔ)收費(fèi)的模型成熟后,可以重新啟用這個(gè)設(shè)計(jì)。
2. 使用適配后的 chainx-org/ink 編寫合約。 chainx-org/ink fork 自 paritytech/ink, 主要改動(dòng)在于引入了 DefaultXrmlTypes 替代 DefaultRrmlTypes來適配 ChainX 的 runtime:
· 關(guān)聯(lián)類型 Balance 從 u128 改為 u64.
· 適配關(guān)聯(lián)類型 Call 以支持 PCX 轉(zhuǎn)賬和將跨鏈資產(chǎn)劃轉(zhuǎn)過去的 XRC20 Token 轉(zhuǎn)回 ChainX 跨鏈資產(chǎn), 合約開發(fā)者可以基于 PCX 和 ChainX 的跨鏈資產(chǎn)設(shè)計(jì)自己的 Dapp。
除了以上兩點(diǎn)改動(dòng),在合約編寫上與 paritytech/ink 保持一致,目前合約寫法仍然采用 ink1.0 的寫法,后續(xù)我們將會(huì)升級(jí)使用 ink2.0.
ink 合約編寫相關(guān)資源:
· substrate-contracts-workshop: ink 官方教程
· polkaworld-org/workshop: polkaworld 合約相關(guān)技術(shù)講座
與 Gas 相關(guān)的參數(shù)配置
Schedule {
version: 0, // 配置版本
put_code_per_byte_cost: 200, // 設(shè)置wasm代碼時(shí),每字節(jié)需要的Gas
grow_mem_cost: 1, // 單頁中內(nèi)存增值需要的Gas
regular_op_cost: 1, // 普通操作符Op需要的Gas
return_data_per_byte_cost: 1, // 返回值每字節(jié)消耗的Gas,因此對(duì)于合約見互相調(diào)用應(yīng)仔細(xì)設(shè)計(jì)這個(gè)值
event_data_per_byte_cost: 20, // 合約中 event 每字節(jié)消耗的Gas
event_per_topic_cost: 1, // 合約中 event 每個(gè)topic消耗的Gas
event_base_cost: 1, // 合約中每個(gè)event要消耗的基礎(chǔ)Gas,例如每打一個(gè)event就要先減少這個(gè)值的Gas
call_base_cost: 60000, // 調(diào)用合約或合約調(diào)用合約消耗的基礎(chǔ)Gas,例如每調(diào)用一個(gè)合約函數(shù)需要減少這個(gè)值的Gas
instantiate_base_cost: 200000, // 合約初始化(實(shí)例化)消耗的基礎(chǔ)Gas
sandbox_data_read_cost: 1, // 合約中讀取一個(gè)字節(jié)消耗的Gas
sandbox_data_write_cost: 1, // 合約中寫入一個(gè)字節(jié)消耗的Gas
max_event_topics: 4, // 一次調(diào)用中最多可以打的event個(gè)數(shù)
max_stack_height: 64 * 1024, // 合約中棧的最大值
max_memory_pages: 16, // 合約執(zhí)行中最大的頁數(shù)
max_table_size: 16 * 1024, // 合約中最大數(shù)據(jù)結(jié)構(gòu)表數(shù)
enable_println: false, // 是否允許合約中出現(xiàn) print
max_subject_len: 32, // 最大的PRNG subject數(shù)
}
當(dāng)前 ChainX 中默認(rèn)的 Gas Price 為5
其中請(qǐng)留意put_code_per_byte_cost,call_base_cost,instantiate_base_cost。因此在 ChainX 中:
· 設(shè)置 WASM 合約代碼的代價(jià)比較大,因此建議合約開發(fā)者仔細(xì)設(shè)計(jì)及模塊化自己的合約代碼,不鼓勵(lì)過多的設(shè)置合約代碼
· 實(shí)例化一個(gè)合約的代價(jià)稍大,因此建議合約開發(fā)者盡量重用已實(shí)例化過的合約實(shí)例
· 調(diào)用一個(gè)合約的代價(jià)一般,因此建議合約開發(fā)者精簡(jiǎn)自己的合約,集中在一個(gè)合約中處理邏輯。
· Event 最多只能打4個(gè),因此需要合約開發(fā)者小心設(shè)計(jì) Event。
部署及調(diào)用合約
對(duì)于部署及調(diào)用合約,請(qǐng)參考 ChainX 的錢包相關(guān)部分:
· 合約功能部分
· 合約開發(fā)獨(dú)立部署組件開發(fā)調(diào)試合約
開發(fā)調(diào)試合約
我們強(qiáng)烈建議:
1. 合約開發(fā)者在 ChainX Dev 節(jié)點(diǎn)下開發(fā)調(diào)試合約,因?yàn)楹芏噱e(cuò)誤信息只會(huì)在節(jié)點(diǎn)日志中出現(xiàn)。
2. 在 Dev 模式下可以在合約中使用env.println,而在主網(wǎng)和測(cè)試網(wǎng)中一定要將合約中的env.println刪除。
3. 合約無論是執(zhí)行還是方法的返回值都可以通過 RPC 在不打包的情況下調(diào)用,建議合約開發(fā)者可以先使用 rpc 調(diào)用模擬合約執(zhí)行情況
一些異常錯(cuò)誤
日志中與合約相關(guān)的日志均含有[runtime|xrml_xcontracts]字段,因此若只關(guān)心合約及合約執(zhí)行結(jié)果,只需要對(duì)日志 grep:
· tail -f log/chainx.log | egrep “xcontracts|apply_extrinsic”
若設(shè)置,部署,調(diào)用合約不成功,可以參考如下問題列表:
1. 設(shè)置合約代碼相關(guān)
1. 合約部署時(shí) gas limit 提供的不夠
[runtime|chainx_runtime::xexecutive|183L] [apply_extrinsic] failed: there is not enough gas for storing the code
因?yàn)?ChainX 中put_code_per_byte_cost設(shè)置得比較大,因此合約部署者應(yīng)注意部署合約時(shí)提供的 gas limit 是否足夠。一般情況下應(yīng)大于len(wasm) * put_code_per_byte_cost
2. wasm 合約時(shí),在組件間調(diào)用時(shí)因 wasm 過大,導(dǎo)致 wasm 被裁剪,部署不完整 wasm
[runtime|chainx_runtime::xexecutive|183L] [apply_extrinsic] failed: Can‘t decode wasm code
此時(shí)請(qǐng)檢查組件調(diào)用是否會(huì)裁剪 wasm
3. wasm 合約已經(jīng)存在于鏈上,對(duì)于相同的合約,不應(yīng)該重復(fù)設(shè)置代碼
[runtime|chainx_runtime::xexecutive|183L] [apply_extrinsic] failed: the code is already stored on chain
開發(fā)者自己編寫工具時(shí),請(qǐng)一定在上傳代碼前通過 sdk 的相關(guān)接口檢查合約是否已經(jīng)在鏈上。
4. 合約中含有ext.print,在自己測(cè)試能部署,在測(cè)試網(wǎng)與主網(wǎng)上不能部署
[runtime|chainx_runtime::xexecutive|183L] [apply_extrinsic] failed: module imports `ext_println` but debug features disabled
請(qǐng)注意在測(cè)試網(wǎng)和主網(wǎng)中,一定要將ext.print從合約中刪除,或者使用條件編譯控制
2. 部署合約代碼相關(guān)
1. 合約實(shí)例已經(jīng)存在,請(qǐng)勿重復(fù)實(shí)例化。
[runtime|chainx_runtime::xexecutive|183L] [apply_extrinsic] failed: Alive contract or tombstone already exists
在 ChainX 合約(Substrate Contracts)模型中,生成合約地址的方式為:
hash(code_hash + hash(input_data) + deployer)
因此相同的部署者對(duì)于相同的一份合約使用相同的實(shí)例化參數(shù),最后的合約結(jié)果都是一樣的。若需要用相同的 wasm 代碼,相同的參數(shù)實(shí)例化另一個(gè)實(shí)例,請(qǐng)換一個(gè)賬戶
2. 參數(shù)傳遞錯(cuò)誤,或者在實(shí)例化過程中崩潰,無法實(shí)例化
[runtime|chainx_runtime::xexecutive|183L] [apply_extrinsic] failed: during execution|Failed to invoke an exported function for some reason|wrong selector, decode params fail or inner error
請(qǐng)檢查參數(shù)和合約代碼,如:
· 實(shí)例化接收的參數(shù)是 u128,但是傳遞過去的是 u64
· 合約中的存儲(chǔ)未初始化就進(jìn)行加減操作/溢出/有 panic 異常。..
3. 部署達(dá)到了 gas limit
錯(cuò)誤見下文
3. 調(diào)用合約代碼相關(guān)
1. 合約調(diào)用或內(nèi)部崩潰
[runtime|chainx_runtime::xexecutive|183L] [apply_extrinsic] failed: during execution|Failed to invoke an exported function for some reason|wrong selector, decode params fail or inner error
· 調(diào)用合約傳遞的 selector 不匹配,或參數(shù)編碼錯(cuò)誤
selector 請(qǐng)一定按照編譯合約生成的的abi.json或者old_abi.json去調(diào)用,若使用不存在的 selector 則會(huì)調(diào)用不成功。編碼錯(cuò)誤同理
· 調(diào)用的方法中出現(xiàn)異常,如使用未初始化存儲(chǔ)
struct Incrementer {
value: storage::Value《u32》,
}
impl Deploy for Incrementer {
fn deploy(&mut self) {
// not init value
}
}
impl Incrementer {
/// Flips the current state of our smart contract.
pub(external) fn inc(&mut self, by: u32) {
// 在日志中能看得到這一句日志
env.println(&format?。ā癐ncrementer::inc by = {:?}”, by));
self.value += by; // 調(diào)用未初始化的存儲(chǔ)
}
}
因此需要調(diào)試是因?yàn)閰?shù) /selector 錯(cuò)誤還是合約內(nèi)部出錯(cuò),請(qǐng)?jiān)诤霞s內(nèi)部打日志即可判定
· 溢出或 panic
具體請(qǐng)參考代碼
2. 調(diào)用過程中達(dá)到了 gas limit
[runtime|chainx_runtime::xexecutive|183L] [apply_extrinsic] failed: during execution|Failed to invoke an exported function for some reason|reach gas limit
請(qǐng)通過 rpc 接口chainx_contractCall在非打包過程中去嘗試得到適合的gas limit,建議覆蓋合約執(zhí)行中最遠(yuǎn)的執(zhí)行分支。
通過 grep 日志:
[runtime|xrml_xcontracts::gas] [refund_unused_gas]|account:88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee (5SjUJPJJ.。.)|gas_payment:2500000|refund:0|real cost:2500000|gas_spent:500000
可以看到這次調(diào)用中的 gas 耗費(fèi),其中
注意該部分消耗指的是在合約中的 Gas 執(zhí)行的消耗,真實(shí)支付的 PCX 還要再加上調(diào)用合約方法的外部手續(xù)費(fèi)。
· gas_payment為gas_limit*gas_price的值,是調(diào)用者暫存的 PCX
· refund是執(zhí)行完成后返回的 PCX
· real cost是真實(shí)消耗的PCX
· gas_spent是消耗的 gas
ChainX 智能合約中的跨鏈資產(chǎn)
當(dāng)前 ChainX 智能合約中只支持比特幣
ChainX 智能合約中多幣種的實(shí)現(xiàn)方式
由于 Substrate Contracts 智能合約平臺(tái)模型與以太坊智能合約模型相似,對(duì)于多幣種的處理是“本幣+代幣”模型。
針對(duì)這種模型,ChainX 的智能合約上的跨鏈資產(chǎn)采用合約代幣模型,因此在 ChainX 上 PCX 將會(huì)類似以太坊一樣對(duì)智能合約提供 gas 及本幣流通的價(jià)值,對(duì)于 ChainX 上的跨鏈資產(chǎn)以合約代幣的形式引入。
使用代幣模型而不是將多幣種直接引入合約基于如下考慮:
1. 以太坊代幣合約已經(jīng)比較成熟,因此對(duì)于合約開發(fā)者在代幣模型下可以比較容易的將以太坊合約遷移到 ChainX 智能合約平臺(tái)上。
2. 若在合約中引入多幣種的概念,將會(huì)極大修改 Substrate Contracts 的模型,容易引入非預(yù)期問題。
當(dāng)前 ChainX 實(shí)現(xiàn)智能合約中的比特幣采用 XRC20 模型,根據(jù)合約開發(fā)者的反饋,將來也可采用其他標(biāo)準(zhǔn)。目前已經(jīng)考慮的代幣模型有:
XRC20 (原以太坊 ERC20)
XRC721 (原以太坊 ERC721)
XRC777 (原以太坊 ERC777)
ChainX 資產(chǎn)中的多幣種與合約中代幣的轉(zhuǎn)換
ChainX 上的比特幣 X-BTC
當(dāng)前 X-BTC 對(duì)應(yīng)于智能合約平臺(tái)中采用的代幣模型為 XRC20。用戶可以自由發(fā)起交易讓 ChainX 的 X-BTC 與合約平臺(tái)中的 XRC20 互相轉(zhuǎn)換。
其中 ChainX 修改了 ERC20 合約標(biāo)準(zhǔn)(XRC20),添加了issue,destroy兩個(gè)接口。并首先將一個(gè)合約部署到鏈上并實(shí)例化,同時(shí)在鏈上唯一指定了這個(gè)合約實(shí)例,因此除指定的實(shí)例以外的代幣實(shí)例均不被 ChainX 的 Runtime 承認(rèn)
其中:
· issue 只能被 Runtime 中的交易convert_to_xrc20調(diào)用,不可通過直接調(diào)用合約方法調(diào)用。通過交易convert_to_xrc20調(diào)用后,將會(huì)把該用戶的資產(chǎn)轉(zhuǎn)移到合約實(shí)例下,并在合約中對(duì)該發(fā)送交易的賬戶自動(dòng)發(fā)放相應(yīng)的金額。
· destroy 能被用戶調(diào)用,用于將合約中的資產(chǎn)轉(zhuǎn)移到 ChainX 資產(chǎn)模塊中。其中首先銷毀了合約中對(duì)應(yīng)的代幣,然后合約中會(huì)自動(dòng)調(diào)用convert_to_assets將資產(chǎn)從合約中返回給用戶,而convert_to_assets不可通過外部交易調(diào)用。
ChainX 上的 XRC20
XRC20 項(xiàng)目
部署于 ChainX 上的 XRC20 項(xiàng)目為:
https://github.com/chainx-org/xrc20
針對(duì) XRC20,ChainX 已經(jīng)提供了2個(gè)對(duì)應(yīng)的 RPC 可進(jìn)行操作:
· chainx_contractXRCTokenInfo
· chainx_contractXRC20Call
ChainX 錢包導(dǎo)入 XRC20 abi
當(dāng)前錢包還未集成 XRC20 的 abi,因此需要開發(fā)者手動(dòng)添加。
1. 獲取鏈上指定的 XRC20-BTC 合約實(shí)例地址:
調(diào)用 rpc chainx_contractXRCTokenInfo 可在返回值中看到鏈上已經(jīng)存在的 XRC20-BTC 合約地址公鑰
2. 參考[合約功能部分|實(shí)例化合約]( wallet#2. 實(shí)例化合約(部署合約))部分的第3節(jié),通過“添加已存在的合約”的方式,將該 ERC20-BTC 合約地址的公鑰填入,然后在 XRC20 項(xiàng)目中的target/abi.json的 abi 文件上傳即可添加該合約實(shí)例。
主網(wǎng)與測(cè)試網(wǎng)-道
對(duì)于主網(wǎng)與測(cè)試網(wǎng)-道,該針對(duì) X-BTC 的合約實(shí)例已經(jīng)部署且被設(shè)置完成。合約開發(fā)者或用戶只需要在合約平臺(tái)上導(dǎo)入該項(xiàng)目中的abi 文件target/abi.json即可調(diào)用合約。其中該XRC20的地址可以通過rpc chainx_contractXRCTokenInfo 獲取到。
ChainX Dev 節(jié)點(diǎn)
對(duì)于 ChainX Dev 節(jié)點(diǎn)而言,該合約沒有被內(nèi)置。因此若需要調(diào)試和智能合約上的比特幣相關(guān)操作,需要:
· 在 ChainX Dev 中設(shè)置 XRC20-BTC 合約
· 發(fā)放測(cè)試使用的假 X-BTC
在xrc20項(xiàng)目中已經(jīng)提供了一個(gè)腳本執(zhí)行這兩件事情,詳情請(qǐng)參考 README。
該腳本默認(rèn)使用 Alice 的私鑰(見 ChainX Dev 模式的開頭部分)執(zhí)行。請(qǐng)注意執(zhí)行該腳本至少需要等待已經(jīng)出了150個(gè)區(qū)塊后( Alice 才會(huì)具備資產(chǎn)執(zhí)行交易)。
若 PCX 不足,該腳本也提供了從 Alice 驗(yàn)證者領(lǐng)取獎(jiǎng)池的操作。
責(zé)任編輯;zl
評(píng)論
查看更多