超块链的智能合约可分为合约库和合约过程两类:
- 合约库:可部署在区块链上供其它合约调用;
- 合约过程:可以被执行,可以调用其他合约过程或合约库,可以被其它合约库或过程调用,可以读取/输出合约执行结果,输出的执行结果可以上链存储供其它合约读取。
程序样例
合约库样例:定义一个合约类,并导出该类作为合约库,供其它智能合约代码调用。
class Student2 { //定义一个类Student2
constructor(name, sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
}
toString() { //自定义格式化输出,可供执行结果链上存储
return 'The Student v2 is ' + this.name + ', and sex is ' + this.sex + ',age is ' + this.age;
}
}
export {Student2} //声明Student2可被其它合约代码导入使用,关键语句。如果缺失,Student2无法被其他合约导入使用。
注:合约库编译后的字节码部署上链发布之后,发布者将获得该合约库的区块链地址(超块链区块地址三元组和区块哈希),调用合约可以通过该区块地址导入合约库。
合约过程样例:导入已部署在区块链上的合约库,调用执行创建Student2实例,调用另一个智能合约过程,最后输出运算结果。
contract.importas( //导入要调用的合约库
[443,1,1],
"d1cad57bab030f3e897f322242e8706591f3d1aff0757a555115372859cd762d",
"StudentEx"
);
let st1 = new StudentEx.Student2('Tom','male', 18);
let st2 = contract.call([3,2,6], '');//调用区块[3,2,6]中的合约过程
st1.toString() + "\n" + st2.toString();//执行结果格式化输出
注:contract合约类用于合约代码的模块化组织,是超块链智能合约语言的基础类之一,它提供importas方法导入合约库,call方法调用其它合约过程。
contract类的声明如下:
class contract {
JSValue importas(
vector<int> tripleaddr,
const std::string& localblkhash,
const string& modulename
);
JSValue call(
vector<int> tripleaddr,
const std::string& localblkhash
);
};
注:其中,importas方法的参数中,
- tripleaddr为超块链中子链区块的三元组地址,方法从指定的区块中获取合约模块导入到当前合约中供后续调用使用。
- localblkhash是三元组地址对应的区块hash,用于区块校验,也可以留空。
- modulename是导入合约库的别名,可任意设定为字符串,用于解决多个合约库间可能的名字冲突,避免执行结果异常。
其中,call方法,用于直接运行指定区块中的合约过程;
此外,合约过程执行完成后,执行结果可通过语句st1.toString() + “\n” + st2.toString();实现格式化输出,应用程序可通过Paralism节点接口将合约过程提交到Paralism节点的虚拟机中运行,提交的合约过程字节码和格式化输出的执行结果在执行完成后将由节点自动提交上链存储。
开发环境准备
超块链网络中至少有两个节点在线运行,并且互已加入对方邻居节点。节点软件版本0.6.0以上。
注:关于节点配置与运行请参考《超块链核心节点CLI操作手册》。
运行环境注意事项
目前,超块链网络单个区块数据负载设定为2M字节,所以
- 单个合约库或过程编译后字节码应<2M字节。
- 过程合约代码字节码与执行结果格式化输出内容应<2M字节。
超块链网络缺省的上链周期默认设置为2.5分钟。
编译与调试
常见的Javascript集成开发环境都可以很方便的支持智能合约的编译与模拟调试。目标运行环境的调试可通过Paralism节点提供的Restful API服务接口EstimateScript来完成:
- EstimateScript
发送智能合约代码给节点虚拟机,返回执行结果。
注:关于节点Restful接口的访问请参考《Paralism节点Restapi接口文档》。
部署与执行
智能合约的部署和执行可通过下面的Restful API接口来完成:
- SubmitRegistrationEx
提交智能合约代码部署上链,返回上链交易的查询句柄。合约部署的状态和结果可通过RestAPI接口GetOnchainState获取。 - SubmitScriptResult
发送智能合约代码给节点,节点执行后,格式化输出结果和代码字节码一起自动提交上链。
开发示例
第一步,编写智能合约库
class Student2 {
constructor(name,sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
}
toString() {
return 'The Student v2 is ' + this.name + ', and sex is ' + this.sex + ',age is ' + this.age;
}
}
export {Student2}
第二步,使用SubmitRegistrationEx部署合约库到区块链
调用样例:http://127.0.0.1:8081/SubmitRegistrationEx
使用http请求 post方法提交时,将合约代码注入到表单form name=script 的input元素中即可。
调试使用postman 工具时,选择Body/form-data方式,增加2个key值:payload 和 script,其中script 的 value设为合约代码,palyload的value可任意填写,如下图:
SubmitRegistrationEx调用得到返回值如下:
{
"queuenum": "1",
"requestid": "25gK2mxmsFJUQvE3PnRpa7cJVscf",
"state": "queueing"
}
然后使用Restful接口调用GetOnchainState查询合约部署交易的执行状态。
http://127.0.0.1:8081/GetOnchainState?requestid=25gK2mxmsFJUQvE3PnRpa7cJVscf ,GetOnchainState查询直到获得以下返回值:
{
"hyperBlockId": 443,
"chainNumber": 1,
"localBlockId": 1,
"onChainState": "onchained"
}
返回结果指示合约代码的字节码被部署到了超块链三元组地址为[443,1,1]的子链区块里。
第三步,导入调用已部署的合约库
编写合约过程,调用已部署合约模块:
contract.importas(
[443,1,1],
"d1cad57bab030f3e897f322242e8706591f3d1aff0757a555115372859cd762d",
"StudentEx"
);
let st1 = new StudentEx.Student2('Tom', 'male', 18);
let st2 = contract.call([3,2,6], '');
st1.toString() + "\n" + st2.toString();
第四步,调试合约过程
- Restful方式调试
调用样例:http://127.0.0.1:8081/EstimateScript
使用http请求 post方法提交时,将合约过程代码注入到Body元素中即可。
如果使用postman工具,选择Body raw方式,调用如下图: - hc控制台方式调试
将合约过程代码保存为文件*.js,用vm run -f 文件名来运行调试,也可在超块链节点软件hyperchain控制台直接使用vm run + 合约代码来调试。
第五步,使用SubmitScriptResult执行合约过程
Restful接口SubmitScriptResult 负责将合约过程编译成字节码执行,并将格式化输出的结果上链存储到子链区块中,该区块中Playload部分存放合约过程编译后的字节码和格式化输出结果。
如果用Postman工具提交合约代码,将提交的代码放到body中采用raw格式即可,如下图:
返回结果如下:
{
"queuenum": "1",
"requestid": "3CX5PXt1eYUwc4VQncZK63hSpmML",
"state": "queueing"
}