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