融安链智能合约的编写
第一次接触融安区块链,其实融安链就是基于webase平台进行二次开发(简称盗版),但是里面还是相对于webase平台有很多坑
我这次编写智能合约就发现融安链在Table工具类就不支持,一直报status:16 其实就是交易回滚的错,通过查询fisco的官网,
描述这个错误是什么合约的代码逻辑,合约没有初始化等等,但是其实都不是,我是通过一点点打印日志来排查的
到最后我才发现是不能很好支持Table通用的工具类的方法调用
一、在融安链错误的案例,但是在天河链、webase平台运行是没有问题的
首先是Table通用工具类
pragma solidity ^0.4.0; contract TableFactory { function openTable(string) public constant returns (Table); // 打开表 function createTable(string,string,string) public returns(int); // 创建表 } // 查询条件 contract Condition { //等于 function EQ(string, int) public; function EQ(string, string) public; //大于 function GT(string, int) public; //大于或等于 function GE(string, int) public; //小于 function LT(string, int) public; //小于或等于 function LE(string, int) public; //限制返回记录条数 function limit(int) public; function limit(int, int) public; } // 单条数据记录 contract Entry { function getInt(string) public constant returns(int); function getAddress(string) public constant returns(address); function getBytes64(string) public constant returns(byte[64]); function getBytes32(string) public constant returns(bytes32); function getString(string) public constant returns(string); function set(string, int) public; function set(string, string) public; function set(string, address) public; } // 数据记录集 contract Entries { function get(int) public constant returns(Entry); function size() public constant returns(int); } // Table主类 contract Table { // 查询接口 function select(string, Condition) public constant returns(Entries); // 插入接口 function insert(string, Entry) public returns(int); // 更新接口 function update(string, Entry, Condition) public returns(int); // 删除接口 function remove(string, Condition) public returns(int); function newEntry() public constant returns(Entry); function newCondition() public constant returns(Condition); }
下面是我的主要核心功能的智能合约
pragma solidity ^0.4.2; pragma experimental ABIEncoderV2; import "./Table.sol"; contract GeneralContract{ address public platform; TableFactory tf = TableFactory(0x1001); // TableFactory的地址固定为0x1001 string constant TABLE_NAME = "general_info"; //约束条件--平台方 modifier onlyPlatform{ require(msg.sender==platform); _; } constructor() public { platform =msg.sender; create(); } event DebugLog(string message, bool value); // 创建表 function create() returns(int){ int count = tf.createTable(TABLE_NAME, "type_tablename_id", "version,status,data"); return count; } //查询操作 function select(string memory type_tablename_id) public view returns(Entry){ Table table = tf.openTable(TABLE_NAME); Condition condition = table.newCondition(); condition.EQ("type_tablename_id", type_tablename_id); Entries entries = table.select(type_tablename_id, condition); require(entries.size() == 1, "DATA_NOT_EXIST"); return entries.get(0); } /** * 描述: 表名称+唯一id组合主键查询记录 */ function selectById(string memory type_tablename_id) public view returns (string memory,string memory, string memory, string memory) { Entry entry = select(type_tablename_id); return (strConcat("type_tablename_id:",entry.getString("type_tablename_id")), strConcat("version:",entry.getString("version")), strConcat("status:",entry.getString("status")), strConcat("data:",entry.getString("data"))); } /** * 描述: 插入记录. */ function insert(string memory type_tablename_id, string memory version, int status,string memory data) public returns (int) { emit DebugLog("Insert started", false); // 确保首个日志触发 bool exists = checkRepeat(type_tablename_id); emit DebugLog("Before require", exists); // 添加日志 //字段校验 require(!exists,"上链数据type_tablename_id已经存在"); Table table = tf.openTable(TABLE_NAME); emit DebugLog("openTable", false); Entry entry = table.newEntry(); emit DebugLog("setdata", false); entry.set("type_tablename_id",type_tablename_id); entry.set("version",version); entry.set("status",status); entry.set("data",data); int count = table.insert(type_tablename_id, entry); emit DebugLog("Insert completed", true); return count; } /** * 描述: 修改记录 */ function update(string memory type_tablename_id, string memory version,int256 status,string memory data) public returns (int256) { //检查字段是否为空 require(bytes(type_tablename_id).length>0,"type_tablename_id不能为空"); //验证更新的数据是否存在 require(checkRepeat(type_tablename_id),"数据不存在,信息更新失败"); Table table = tf.openTable(TABLE_NAME); Entry entry = table.newEntry(); entry.set("version", version); entry.set("status", status); entry.set("data", data); Condition condition = table.newCondition(); condition.EQ("type_tablename_id", type_tablename_id); int256 count = table.update(type_tablename_id, entry, condition); return count; } /** * 描述: 移除记录 */ function remove(string memory type_tablename_id) public returns (int256) { Table table = tf.openTable(TABLE_NAME); Condition condition = table.newCondition(); condition.EQ("type_tablename_id", type_tablename_id); int256 count = table.remove(type_tablename_id, condition); return count; } //检查记录重复 function checkRepeat(string memory type_tablename_id) public returns(bool){ Table table = tf.openTable(TABLE_NAME); emit DebugLog("openTable", false); Entry entry = table.newEntry(); emit DebugLog("newEntry", false); Condition condition = table.newCondition(); condition.EQ("type_tablename_id", type_tablename_id); Entries record = table.select(type_tablename_id,condition); emit DebugLog("Records size", record.size() > 0); // 添加日志 return record.size()>0; } //字符串拼接 function strConcat(string memory _a, string memory _b) public returns (string memory){ bytes memory _ba = bytes(_a); bytes memory _bb = bytes(_b); string memory ret = new string(_ba.length + _bb.length); bytes memory bret = bytes(ret); uint k = 0; for (uint i = 0; i < _ba.length; i++)bret[k++] = _ba[i]; for (uint j = 0; j < _bb.length; j++) bret[k++] = _bb[j]; return string(ret); } }
在融安链运行数据上链就会报这个错误
二、在融安链就应该避免使用Table工具类来完成你的业务
下面是我的两个例子,其实思路都一样,只是根据我这边的业务需求,一个合约存放的是常规数据,另外一个智能合约存放的是有加密信息的交易数据
存放常规数据的智能合约
pragma solidity >=0.4.24 <0.6.11; contract GenaralInfo{ /** * @dev 记录结构体定义 * @param type_tablename_id 主键标识(唯一) * @param version 记录版本号 * @param status 记录状态(整型) * @param data 记录数据内容 */ struct Record { string type_tablename_id; string version; int256 status; string data; } // 主键到记录的映射(私有) mapping(string => Record) private general_info; // 事件定义 event RecordAdded(string indexed id); // 记录添加事件 event RecordUpdated(string indexed id); // 记录更新事件 event RecordDeleted(string indexed id); // 记录删除事件 /** * @dev 检查记录是否存在 * @param id 要检查的主键 * @return bool 存在返回true,否则false */ function isRecordExists(string memory id) public view returns (bool) { // 通过检查主键字段长度判断记录是否存在 return bytes(general_info[id].type_tablename_id).length != 0; } /** * @dev 添加新记录 * @param id 主键(必须唯一) * @param version 版本号 * @param status 状态值 * @param data 数据内容 * @return bool 成功返回true * 要求:主键不能为空且不能重复 */ function insert( string memory id, string memory version, int256 status, string memory data ) public returns (bool) { // 校验1:主键不能为空 require(bytes(id).length > 0, "主键不能为空"); // 校验2:主键必须不存在 require(!isRecordExists(id), "记录已存在"); // 创建新记录 general_info[id] = Record(id, version, status, data); // 触发添加事件 emit RecordAdded(id); return true; } /** * @dev 更新现有记录 * @param id 要更新的主键 * @param version 新版本号 * @param status 新状态值 * @param data 新数据内容 * @return bool 成功返回true * 要求:记录必须存在 */ function update( string memory id, string memory version, int256 status, string memory data ) public returns (bool) { // 校验:记录必须存在 require(isRecordExists(id), "记录不存在"); // 更新记录字段 Record storage record = general_info[id]; record.version = version; record.status = status; record.data = data; // 触发更新事件 emit RecordUpdated(id); return true; } /** * @dev 删除记录 * @param id 要删除的主键 * @return bool 成功返回true * 要求:记录必须存在 */ function remove(string memory id) public returns (bool) { // 校验:记录必须存在 require(isRecordExists(id), "记录不存在"); // 从映射中删除记录 delete general_info[id]; // 触发删除事件 emit RecordDeleted(id); return true; } /** * @dev 查询单条记录 * @param id 要查询的主键 * @return 返回记录的四个字段 * 要求:记录必须存在 */ function selectById(string memory id) public view returns ( string memory, string memory, string memory, string memory ) { // 校验:记录必须存在 require(isRecordExists(id), "记录不存在"); Record memory record = general_info[id]; return ( strConcat("type_tablename_id:",record.type_tablename_id), strConcat("version:",record.version), strConcat("status:",intToString(record.status)), strConcat("data:",record.data) ); } //字符串拼接 function strConcat(string memory _a, string memory _b) public returns (string memory){ bytes memory _ba = bytes(_a); bytes memory _bb = bytes(_b); string memory ret = new string(_ba.length + _bb.length); bytes memory bret = bytes(ret); uint k = 0; for (uint i = 0; i < _ba.length; i++)bret[k++] = _ba[i]; for (uint j = 0; j < _bb.length; j++) bret[k++] = _bb[j]; return string(ret); } //int转string 类型 function intToString(int256 _value) internal pure returns (string memory) { if (_value == 0) { return "0"; } bool isNegative = _value < 0; uint256 absValue = isNegative ? uint256(-_value) : uint256(_value); // 计算数字位数(包括负号) uint256 digits = 0; uint256 temp = absValue; while (temp != 0) { digits++; temp /= 10; } if (isNegative) { digits++; // 为负号预留位置 } bytes memory buffer = new bytes(digits); uint256 index = digits - 1; // 处理负数 if (isNegative) { buffer[0] = '-'; } // 填充数字 while (absValue != 0) { buffer[index] = bytes1(uint8(48 + (absValue % 10))); absValue /= 10; index--; } return string(buffer); } }
存放交易数据的智能合约
pragma solidity >=0.4.24 <0.6.11; contract Transaction{ /** * @dev 记录结构体定义 * @param type_tablename_id 主键标识(唯一) * @param version 记录版本号 * @param status 记录状态(整型) * @param data 记录数据内容 */ struct Record { string type_tablename_id; string version; int256 status; string data; } // 主键到记录的映射(私有) mapping(string => Record) private transaction_info; // 事件定义 event RecordAdded(string indexed id); // 记录添加事件 event RecordUpdated(string indexed id); // 记录更新事件 event RecordDeleted(string indexed id); // 记录删除事件 /** * @dev 检查记录是否存在 * @param id 要检查的主键 * @return bool 存在返回true,否则false */ function isRecordExists(string memory id) public view returns (bool) { // 通过检查主键字段长度判断记录是否存在 return bytes(transaction_info[id].type_tablename_id).length != 0; } /** * @dev 添加新记录 * @param id 主键(必须唯一) * @param version 版本号 * @param status 状态值 * @param data 数据内容 * @return bool 成功返回true * 要求:主键不能为空且不能重复 */ function insert( string memory id, string memory version, int256 status, string memory data ) public returns (bool) { // 校验1:主键不能为空 require(bytes(id).length > 0, "主键不能为空"); // 校验2:主键必须不存在 require(!isRecordExists(id), "记录已存在"); // 创建新记录 transaction_info[id] = Record(id, version, status, data); // 触发添加事件 emit RecordAdded(id); return true; } /** * @dev 更新现有记录 * @param id 要更新的主键 * @param version 新版本号 * @param status 新状态值 * @param data 新数据内容 * @return bool 成功返回true * 要求:记录必须存在 */ function update( string memory id, string memory version, int256 status, string memory data ) public returns (bool) { // 校验:记录必须存在 require(isRecordExists(id), "记录不存在"); // 更新记录字段 Record storage record = transaction_info[id]; record.version = version; record.status = status; record.data = data; // 触发更新事件 emit RecordUpdated(id); return true; } /** * @dev 删除记录 * @param id 要删除的主键 * @return bool 成功返回true * 要求:记录必须存在 */ function remove(string memory id) public returns (bool) { // 校验:记录必须存在 require(isRecordExists(id), "记录不存在"); // 从映射中删除记录 delete transaction_info[id]; // 触发删除事件 emit RecordDeleted(id); return true; } /** * @dev 查询单条记录 * @param id 要查询的主键 * @return 返回记录的四个字段 * 要求:记录必须存在 */ function selectById(string memory id) public view returns ( string memory, string memory, string memory, string memory ) { // 校验:记录必须存在 require(isRecordExists(id), "记录不存在"); Record memory record = transaction_info[id]; return ( strConcat("type_tablename_id:",record.type_tablename_id), strConcat("version:",record.version), strConcat("status:",intToString(record.status)), strConcat("data:",record.data) ); } //字符串拼接 function strConcat(string memory _a, string memory _b) public returns (string memory){ bytes memory _ba = bytes(_a); bytes memory _bb = bytes(_b); string memory ret = new string(_ba.length + _bb.length); bytes memory bret = bytes(ret); uint k = 0; for (uint i = 0; i < _ba.length; i++)bret[k++] = _ba[i]; for (uint j = 0; j < _bb.length; j++) bret[k++] = _bb[j]; return string(ret); } //int转string 类型 function intToString(int256 _value) internal pure returns (string memory) { if (_value == 0) { return "0"; } bool isNegative = _value < 0; uint256 absValue = isNegative ? uint256(-_value) : uint256(_value); // 计算数字位数(包括负号) uint256 digits = 0; uint256 temp = absValue; while (temp != 0) { digits++; temp /= 10; } if (isNegative) { digits++; // 为负号预留位置 } bytes memory buffer = new bytes(digits); uint256 index = digits - 1; // 处理负数 if (isNegative) { buffer[0] = '-'; } // 填充数字 while (absValue != 0) { buffer[index] = bytes1(uint8(48 + (absValue % 10))); absValue /= 10; index--; } return string(buffer); } }
后面两个智能合约在融安链运行是没有问题的,实测过,欢迎喜欢区块链的小伙伴一起交流学习!