标签:存在 mss 定义 ons bre tps 应用 bst host
简介:前几篇文章我们一直在讨论Solidity语言的相关语法,从本文开始,我们将介绍智能合约开发。今天我们将介绍一个完整范例。接口定义
说明:此接口定义了顾客管理合约的基本操作,接口的定义可以开放给三方进行调用而不暴露源码;
文件目录:${workspace}/contracts/interfaces 用于存放抽象合约目录
pragma solidity ^0.4.2;
contract IConsumerManager {
function add(string _mobile, string _name, string _account, string _remark) public returns(uint);
function deleteByMobile(string _mobile) public returns(uint);
function listAll() constant public returns (string _json);
}
数据结构定义
说明:当接口中的输入输出数据项比较多,或者存储在链上的数据项比较多时,开发者可以定义一个结构化数据,来简化数据项的声明。并且在这个结构化数据,还可以封装对数据的序列化操作,主要包括通过将json格式转为结构化数据 或 反序列化为json格式。
可以把结构化数据,看成面向对象编程中的对象。
文件目录:${workspace}/contracts/librarys 用于存放数据结构的定义
pragma solidity ^0.4.2;
import "../utillib/LibInt.sol";
import "../utillib/LibString.sol";
import "../utillib/LibStack.sol";
import "../utillib/LibJson.sol";
library LibConsumer {
using LibInt for *;
using LibString for *;
using LibJson for *;
using LibConsumer for *;
struct Consumer {
string mobile;
string name;
string account;
string remark;
}
/**
*@desc fromJson for Consumer
* Generated by juzhen SolidityStructTool automatically.
* Not to edit this code manually.
*/
function fromJson(Consumer storage _self, string _json) internal returns(bool succ) {
_self.reset();
if (!_json.isJson())
return false;
_self.mobile = _json.jsonRead("mobile");
_self.name = _json.jsonRead("name");
_self.account = _json.jsonRead("account");
_self.remark = _json.jsonRead("remark");
return true;
}
/**
*@desc toJson for Consumer
* Generated by juzhen SolidityStructTool automatically.
* Not to edit this code manually.
*/
function toJson(Consumer storage _self) internal constant returns (string _json) {
LibStack.push("{");
LibStack.appendKeyValue("mobile", _self.mobile);
LibStack.appendKeyValue("name", _self.name);
LibStack.appendKeyValue("account", _self.account);
LibStack.appendKeyValue("remark", _self.remark);
LibStack.append("}");
_json = LibStack.pop();
}
/**
*@desc fromJsonArray for Consumer
* Generated by juzhen SolidityStructTool automatically.
* Not to edit this code manually.
*/
function fromJsonArray(Consumer[] storage _self, string _json) internal returns(bool succ) {
_self.length = 0;
if (!_json.isJson())
return false;
while (true) {
string memory key = "[".concat(_self.length.toString(), "]");
if (!_json.jsonKeyExists(key))
break;
_self.length++;
_self[_self.length-1].fromJson(_json.jsonRead(key));
}
return true;
}
/**
*@desc toJsonArray for Consumer
* Generated by juzhen SolidityStructTool automatically.
* Not to edit this code manually.
*/
function toJsonArray(Consumer[] storage _self) internal constant returns(string _json) {
_json = _json.concat("[");
for (uint i=0; i<_self.length; ++i) {
if (i == 0)
_json = _json.concat(_self[i].toJson());
else
_json = _json.concat(",", _self[i].toJson());
}
_json = _json.concat("]");
}
/**
*@desc update for Consumer
* Generated by juzhen SolidityStructTool automatically.
* Not to edit this code manually.
*/
function update(Consumer storage _self, string _json) internal returns(bool succ) {
if (!_json.isJson())
return false;
if (_json.jsonKeyExists("mobile"))
_self.mobile = _json.jsonRead("mobile");
if (_json.jsonKeyExists("name"))
_self.name = _json.jsonRead("name");
if (_json.jsonKeyExists("account"))
_self.account = _json.jsonRead("account");
if (_json.jsonKeyExists("remark"))
_self.remark = _json.jsonRead("remark");
return true;
}
/**
*@desc reset for Consumer
* Generated by juzhen SolidityStructTool automatically.
* Not to edit this code manually.
*/
function reset(Consumer storage _self) internal {
delete _self.mobile;
delete _self.name;
delete _self.account;
delete _self.remark;
}
}
业务合约编写
说明:顾客管理合约的主要业务逻辑,即合约接口的实现类. ConsumerManager.sol,该合约继承了基础合约OwnerNamed以及抽象合约IConsumerManager。
文件目录:${workspace}/contracts 用于存放业务合约主体逻辑
pragma solidity ^0.4.2;
import "./library/LibConsumer.sol";
import "./sysbase/OwnerNamed.sol";
import "./interfaces/IConsumerManager.sol";
import "./interfaces/IUserManager.sol";
import "./utillib/LibLog.sol";
contract ConsumerManager is OwnerNamed, IConsumerManager {
using LibConsumer
for * ;
using LibString
for * ;
using LibInt
for * ;
using LibLog
for * ;
event Notify(uint _errno, string _info);
LibConsumer.Consumer[] consumerList;
mapping(string => uint) keyMap;
//定义错误信息
enum ErrorNo {
NO_ERROR,
BAD_PARAMETER,
MOBILE_EMPTY,
USER_NOT_EXISTS,
MOBILE_ALREADY_EXISTS,
ACCOUNT_ALREDY_EXISTS,
NO_PERMISSION
}
// 构造函数,在合约发布时会被触发调用
function ConsumerManager() {
LibLog.log("deploy ConsumerModule....");
//把合约注册到JUICE链上, 参数必须和ConsumerModule.sol中的保持一致
register("ConsumerModule", "0.0.1.0", "ConsumerManager", "0.0.1.0");
//或者注册到特殊的模块"juzix.io.debugModule",这样用户就不需要编写模块合约了
//register("juzix.io.debugModule", "0.0.1.0", "ConsumerManager", "0.0.1.0");
}
function add(string _mobile, string _name, string _account, string _remark) public returns(uint) {
LibLog.log("into add..", "ConsumerManager");
LibLog.log("ConsumerManager into add..");
if (_mobile.equals("")) {
LibLog.log("Invalid mobile.", "ConsumerManager");
errno = 15200 + uint(ErrorNo.MOBILE_EMPTY);
Notify(errno, "顾客手机号为空,插入失败.");
return errno;
}
if (keyMap[_mobile] == 0) {
if (consumerList.length > 0) {
if (_mobile.equals(consumerList[0].mobile)) {
LibLog.log("mobile aready exists", "ConsumerManager");
errno = 15200 + uint(ErrorNo.MOBILE_ALREADY_EXISTS);
Notify(errno, "顾客手机号已存在,插入失败.");
return errno;
}
}
} else {
LibLog.log("mobile aready exists", "ConsumerManager");
errno = 15200 + uint(ErrorNo.MOBILE_ALREADY_EXISTS);
Notify(errno, "顾客手机号已存在,插入失败.");
return errno;
}
uint idx = consumerList.length;
consumerList.push(LibConsumer.Consumer(_mobile, _name, _account, _remark));
keyMap[_mobile] = idx;
errno = uint(ErrorNo.NO_ERROR);
LibLog.log("add a consumer success", "ConsumerManager");
Notify(errno, "add a consumer success");
return errno;
}
function deleteByMobile(string _mobile) public returns(uint) {
LibLog.log("into delete..", "ConsumerManager");
//合约拥有者,才能删除顾客信息
if (tx.origin != owner) {
LibLog.log("msg.sender is not owner", "ConsumerManager");
LibLog.log("operator no permission");
errno = 15200 + uint(ErrorNo.NO_PERMISSION);
Notify(errno, "无操作权限,非管理员");
return;
}
//顾客列表不为空
if (consumerList.length > 0) {
if (keyMap[_mobile] == 0) {
//_mobile不存在,或者是数组第一个元素
if (!_mobile.equals(consumerList[0].mobile)) {
LibLog.log("consumer not exists: ", _mobile);
errno = 15200 + uint(ErrorNo.USER_NOT_EXISTS);
Notify(errno, "顾客手机号不存在,删除失败.");
return;
}
}
} else {
LibLog.log("consumer list is empty: ", _mobile);
errno = 15200 + uint(ErrorNo.USER_NOT_EXISTS);
Notify(errno, "顾客列表为空,删除失败.");
return;
}
//数组总长度
uint len = consumerList.length;
//此用户在数组中的序号
uint idx = keyMap[_mobile];
if (idx >= len) return;
for (uint i = idx; i < len - 1; i++) {
//从待删除的数组element开始,把后一个element移动到前一个位置
consumerList[i] = consumerList[i + 1];
//同时修改keyMap中,对应key的在数组中的序号
keyMap[consumerList[i].mobile] = i;
}
//删除数组最后一个元素(和倒数第二个重复了)
delete consumerList[len - 1];
//删除mapping中元素,实际上是设置value为0
delete keyMap[_mobile];
//数组总长度-1
consumerList.length--;
LibLog.log("delete user success.", "ConsumerManager");
errno = uint(ErrorNo.NO_ERROR);
Notify(errno, "删除顾客成功.");
}
function listAll() constant public returns(string _json) {
uint len = 0;
uint counter = 0;
len = LibStack.push("");
for (uint i = 0; i < consumerList.length; i++) {
if (counter > 0) {
len = LibStack.append(",");
}
len = LibStack.append(consumerList[i].toJson());
counter++;
}
len = itemsStackPush(LibStack.popex(len), counter);
_json = LibStack.popex(len);
}
function itemsStackPush(string _items, uint _total) constant private returns(uint len) {
len = 0;
len = LibStack.push("{");
len = LibStack.appendKeyValue("result", uint(0));
len = LibStack.appendKeyValue("total", _total);
len = LibStack.append(",\"data\":[");
len = LibStack.append(_items);
len = LibStack.append("]");
len = LibStack.append("}");
return len;
}
}
模块合约
说明:模块合约是JUICE区块链中,为了管理用户的业务合约,以及为了管理DAPP和业务的关系而引入的。开发者在实现业务合约后,必须编写一个或多个模块合约,并在模块合约中说明本模块中用到的业务合约。从DAPP的角度来理解,就是一个DAPP必须对应一个模块,一个DAPP能调用的业务合约,必须在DAPP对应的模块合约中说明。
模块合约继承了基础模块合约BaseModule
文件目录:${workspace}/contracts 用于存放业务模块合约主体逻辑
/**
* @file ConsumerModule.sol
* @author JUZIX.IO
* @time 2017-12-11
* @desc 给用户展示如何编写一个自己的模块。
* ConsumerModule本身也是一个合约,它需要部署到链上;同时,它又负责管理用户的合约。只有添加到模块中的用户合约,用户才能在dapp中调用这些合约
*/
pragma solidity ^ 0.4 .2;
//juice的管理库,必须引入
import "./sysbase/OwnerNamed.sol";
import "./sysbase/BaseModule.sol";
//juice提供的模块库,必须引入
import "./library/LibModule.sol";
//juice提供的合约库,必须引入
import "./library/LibContract.sol";
//juice提供的string库
import "./utillib/LibString.sol";
//juice提供的log库
import "./utillib/LibLog.sol";
contract ConsumerModule is BaseModule {
using LibModule
for * ;
using LibContract
for * ;
using LibString
for * ;
using LibInt
for * ;
using LibLog
for * ;
LibModule.Module tmpModule;
LibContract.Contract tmpContract;
//定义Demo模块中的错误信息
enum MODULE_ERROR {
NO_ERROR
}
//定义Demo模块中用的事件,可以用于返回错误信息,也可以返回其他信息
event Notify(uint _code, string _info);
// module : predefined data
function ConsumerModule() {
//定义模块合约名称
string memory moduleName = "ConsumerModule";
//定义模块合约名称
string memory moduleDesc = "顾客模块";
//定义模块合约版本号
string memory moduleVersion = "0.0.1.0";
//指定模块合约ID
//moduleId = moduleName.concat("_", moduleVersion);
string memory moduleId = moduleName.concat("_", moduleVersion);
//把合约注册到JUICE链上
LibLog.log("register DemoModule");
register(moduleName, moduleVersion);
//模块名称,只是JUICE区块链内部管理模块使用,和moduleText有区别
tmpModule.moduleName = moduleName;
tmpModule.moduleVersion = moduleVersion;
tmpModule.moduleEnable = 0;
tmpModule.moduleDescription = moduleDesc;
//显示JUICE开放平台,我的应用列表中的DAPP名字
tmpModule.moduleText = moduleDesc;
uint nowTime = now * 1000;
tmpModule.moduleCreateTime = nowTime;
tmpModule.moduleUpdateTime = nowTime;
tmpModule.moduleCreator = msg.sender;
//这里设置用户DAPP的连接地址(目前DAPP需要有用户自己发布、部署到公网上)
tmpModule.moduleUrl = "http://host.domain.com/youDapp/";
tmpModule.icon = "";
tmpModule.publishTime = nowTime;
//把模块合约本身添加到系统的模块管理合约中。这一步是必须的,只有这样,用户的dapp才能调用添加到此模块合约的相关合约。
//并在用户的“我的应用”中展示出来
LibLog.log("add ConsumerModule to SysModule");
uint ret = addModule(tmpModule.toJson());
if (ret != 0) {
LibLog.log("add ConsumerModule to SysModule failed");
return;
}
//添加用户合约到模块合约中
LibLog.log("add ConsumerManager to ConsumerModule");
ret = initContract(moduleName, moduleVersion, "ConsumerManager", "顾客管理合约", "0.0.1.0");
if (ret != 0) {
LibLog.log("add ConsumerManager to ConsumerModule failed");
return;
}
//返回消息,以便控制台能看到是否部署成功
Notify(1, "deploy ConsumerModule success");
}
/**
* 初始化用户自定义合约。
* 如果用户有多个合约文件,则需要多次调用此方法。
* @param moduleName 约合所属模块名
* @param moduleVersion 约合所属模块版本
* @param contractName 约合名
* @param contractDesc 约合描述
* @param contractVersion 约合版本
* @return return 0 if success;
*/
function initContract(string moduleName, string moduleVersion, string contractName, string contractDesc, string contractVersion) private returns(uint) {
tmpContract.moduleName = moduleName;
tmpContract.moduleVersion = moduleVersion;
//合约名称
tmpContract.cctName = contractName;
//合约描述
tmpContract.description = contractDesc;
//合约版本
tmpContract.cctVersion = contractVersion;
//保持false
tmpContract.deleted = false;
//保持0
tmpContract.enable = 0;
uint nowTime = now * 1000;
//合约创建时间
tmpContract.createTime = nowTime;
//合约修改时间
tmpContract.updateTime = nowTime;
//合约创建人
tmpContract.creator = msg.sender;
//预约块高
tmpContract.blockNum = block.number;
uint ret = addContract(tmpContract.toJson());
return ret;
}
}
o 校验模块开关,开:继续鉴权,关:直接通过
o 校验合约开关,开:继续鉴权,关:直接通过
o 检验函数开关,开:继续鉴权,关:直接通过
o 校验用户是否存在,存在则访问通过,不存在则鉴权失败
注意:如果是合约发布者owner(超级管理员)则不需要鉴权可直接通过。
o 添加一个新的模块到角色过滤器(默认过滤器)
o 添加绑定合约与模块的关系
o 添加菜单(新的DAPP如果需要菜单-如:用户管理)
o 添加权限,合约中的每个函数操作都是一个Action,如果需要访问就需要进行配置;
o 添加角色,初始化某些角色到模块中,并绑定对应的权限到角色上;
编译部署、测试
编译部署
业务合约,模块合约编写完成后
2.1.编译模块合约。编译成功后的的bin/abi,不需要保存。
2.部署模块合约
测试
在JUICE客户端中,选择需要测试的业务合约,以及相应的业务方法,然后填写输入参数,即可运行。用户可观察控制台的日志输出,来判断业务方法是否执行成功。
参考内容:https://open.juzix.net/doc
智能合约开发教程视频:区块链系列视频课程之智能合约简介
标签:存在 mss 定义 ons bre tps 应用 bst host
原文地址:http://blog.51cto.com/13544628/2137202