diff --git a/2021-BeiJing-BSN3/PRG/Contracts/TableDefTools.sol b/2021-BeiJing-BSN3/PRG/Contracts/TableDefTools.sol new file mode 100644 index 000000000..b24136200 --- /dev/null +++ b/2021-BeiJing-BSN3/PRG/Contracts/TableDefTools.sol @@ -0,0 +1,324 @@ +pragma solidity ^0.4.25; +pragma experimental ABIEncoderV2; +import "./lib/Table.sol"; +import "./lib/Strings.sol"; +import "./utils/StringUtil.sol"; +import "./utils/TypeConvertUtil.sol"; +import "./lib/SafeMath.sol"; +import "./utils/TimeUtil.sol"; +import "./TableDefTools.sol"; +/* +* 使用分布式存储下表结构、CURD工具以及预定义常数 +* +*/ +contract TableDefTools { + + /******* 引入库 *******/ + using Strings for *; + using StringUtil for *; + using TypeConvertUtil for *; + using TimeUtil for *; + using SafeMath for *; + + /******* 表结构体 *******/ + struct Bean { + // 表名称 + string tableName; + // 主键 + string primaryKey; + // 表字段 + string[] fields; + } + + /******* 使用到的表结构 *******/ + Bean t_user_struct;//1.用户表 + Bean t_proposal_struct;//2.需求表 + Bean t_report_struct;//3.反馈表 + + + /******* 执行状态码常量 *******/ + int8 constant internal INITIAL_STATE = 0; + int8 constant internal SUCCESS_RETURN = 1; + int8 constant internal FAIL_RETURN = -1; + int8 constant internal FAIL_NULL_RETURN = -2; + int8 constant internal FAIL_ALREADY_EXIST = -3; + int8 constant internal FAIL_INSERT = -4; + int8 constant internal FAIL_LACK_BALANCE = -5; + int8 constant internal FAIL_NO_REWARD = -6; + + /******* 事件状态码常量 *******/ + uint8 constant internal EVENT_GIVE_LIKES = 10; + uint8 constant internal EVENT_OBTAIN_LIKES = 11; + uint8 constant internal EVENT_ATTEND_LOCATION = 12; + + + // 1.用户表 + // 表名称:t_user + // 表主键:user_id + // 表字段:user_name,user_password,activate_time,user_type,registered_status,user_creditpoint; + // 字段含义: + // |------------------------------------------------|-------------------|--------------------------|--------------------|--------------------------| + // | 用户ID(主键) | 用户名 | 登陆密码 | 注册时间 | 身份类别(农民/议员/监察员)| + // |------------------------------------------------|-------------------|--------------------------|--------------------|--------------------------| + // | user_id 【可以采取递增】 | user_name | user_password | activate_time | user_type | + // |------------------------------------------------|-------------------|--------------------------|--------------------|------------------------| + // | 注册状态(0-未激活、1-已激活) | 信用积分(默认100) | + // |------------------------------------------------|----------------------| + // | registered_status | user_creditpoint | + // |------------------------------------------------|----------------------| + //定义表名,表主键,以及表内包含的其他属性值 + string constant internal TABLE_USER_NAME = "t_user"; + string constant internal TABLE_USER_PRIMARYKEY = "user_id"; + string constant internal TABLE_USER_FIELDS = "user_name,user_password,activate_time,user_type,registered_status,user_creditpoint"; + // 2.需求表 + // 表名称:t_proposal + // 表主键:proposal_id + // 表字段:user_id,proposal_content,proposal_time,approval_cnt,disapproval_cnt,check_result; + // 字段含义: + //|------------------- |-------------------|-------------------|-------------------|-------------------|-------------------|-------------------| + //| 留言内容ID号(主键)| 用户ID(主键) | 提议内容 | 提议时间 | 赞成票数 | 反对票数 |审核结果 | + //|--------------------|-------------------|-------------------|-------------------|-------------------|------------------—|------------------—| + //| proposal_id | user_id | proposal_content | proposal_time | approval_cnt |disapproval_cnt | check_result | + //|------------------- |-------------------|-------------------|-------------------|-------------------|-------------------|-------------------| + string constant internal TABLE_PROPOSAL_NAME = "t_proposal"; + string constant internal TABLE_PROPOSAL_PRIMARYKEY = "proposal_id"; + string constant internal TABLE_PROPOSAL_FIELDS = "user_id,proposal_content,proposal_time,approval_cnt,disapproval_cnt,check_result"; + // 3.反馈表 + // 表名称:t_report + // 表主键:report_id + // 表字段:user_id,to_user_id,event_content,event_type + // 字段含义: + // |-------------------|-------------------------------------|---------------------|----------------------| + // | 反馈ID | 用户ID | 反馈的用户对象ID | 事件内容 | 事件类型【举报/好人好事】| + // |-------------------|--------------------|----------------|---------------------|----------------------| + // | report_id | user_id | to_ user_id | event_content |event_type | + // |----------------------------------------|----------------|---------------------|---------------------| + string constant internal TABLE_REPORT_NAME = "t_report"; + string constant internal TABLE_REPORT_PRIMARYKEY = "report_id"; + string constant internal TABLE_REPORT_FIELDS = "user_id,to_user_id,event_content,event_type"; + /* + * 字符串数组生成工具 + * + * @param _fields 带解析的字符串 + * + * @return 解析后的字符串数组 + * + */ + function getFieldsArray(string _fields) internal pure returns(string[]){ + string[] memory arrays ; + Strings.slice memory s = _fields.toSlice(); + Strings.slice memory delim = ",".toSlice(); + uint params_total = s.count(delim) + 1; + arrays = new string[](params_total); + for(uint i = 0; i < params_total; i++) { + arrays[i] = s.split(delim).toString(); + } + return arrays; + } + + + /* + * 1.初始化一个表结构 + * + * @param _tableStruct 表结构体 + * @param _tableName 表名称 + * @param _primaryKey 表主键 + * @param _fields 表字段组成的字符串(除主键) + * + * @return 无 + * + */ + function initTableStruct( + Bean storage _tableStruct, + string memory _tableName, + string memory _primaryKey, + string memory _fields) + internal { + TableFactory tf = TableFactory(0x1001); + tf.createTable(_tableName, _primaryKey, _fields); + _tableStruct.tableName = _tableName; + _tableStruct.primaryKey = _primaryKey; + _tableStruct.fields = new string[](0); + Strings.slice memory s = _fields.toSlice(); + Strings.slice memory delim = ",".toSlice(); + uint params_total = s.count(delim) + 1; + for(uint i = 0; i < params_total; i++) { + _tableStruct.fields.push(s.split(delim).toString()); + } + } + + + /* + * 2.打开一个表,便于后续操作该表 + * + * @param _tableName 表名称 + * + * @return 表 + * + */ + function openTable(string memory _table_name) internal returns(Table) { + TableFactory tf = TableFactory(0x1001); + Table table = tf.openTable(_table_name); + return table; + } + /* + * 3.用户登陆并以Json格式输出用户信息 + * + * @param _tableStruct 表结构 + * @param _primaryKey 待查记录的主键 + * + * @return 执行状态码 + * @return 该记录的json字符串 + */ + function loginInToJson(Bean storage _tableStruct, string memory _primaryKey,string memory _userpasswordhash) internal view returns (int8, string memory) { + // 打开表 + Table table = openTable(_tableStruct.tableName); + Condition condition = table.newCondition(); + condition.EQ("user_id", _primaryKey); + condition.EQ("user_password", _userpasswordhash); + // 查询 + Entries entries = table.select(_primaryKey, condition); + // 将查询结果解析为json字符串 + return StringUtil.getJsonString(_tableStruct.fields, _primaryKey, entries); + } + + /* + * 4.查询表中一条记录并以Json格式输出 + * + * @param _tableStruct 表结构 + * @param _primaryKey 待查记录的主键 + * + * @return 执行状态码 + * @return 该记录的json字符串 + */ + function selectOneRecordToJson(Bean storage _tableStruct, string memory _primaryKey) internal view returns (int8, string memory) { + // 打开表 + Table table = openTable(_tableStruct.tableName); + Condition condition = table.newCondition(); + // 查询 + Entries entries = table.select(_primaryKey, condition); + // 将查询结果解析为json字符串 + return StringUtil.getJsonString(_tableStruct.fields, _primaryKey, entries); + } + + + /* + * 5.查询表中一条记录并以字符串数组的格式输出 + * + * @param _tableStruct 表结构 + * @param _primaryKey 待查记录的主键 + * @param _conditionPair 筛选条件(一个字段) + * + * @return 执行状态码 + * @return 该记录的字符串数组 + */ + function selectOneRecordToArray(Bean storage _tableStruct, string memory _primaryKey, string[2] _conditionPair) internal returns (int8, string[] memory) { + // 打开表 + Table table = openTable(_tableStruct.tableName); + // 查询 + Condition condition = table.newCondition(); + if(_conditionPair.length == 2){ + condition.EQ(_conditionPair[0], _conditionPair[1]); + } + Entries entries = table.select(_primaryKey, condition); + // int8 statusCode = constDef.INITIAL_STATE; + int8 statusCode = 0; + string[] memory retContent; + if (entries.size() > 0) { + // statusCode = constDef.SUCCESS_RETURN; + statusCode = 1; + retContent = StringUtil.getEntry(_tableStruct.fields, entries.get(0)); + + }else{ + statusCode = FAIL_NULL_RETURN; + retContent = new string[](0); + } + return (statusCode ,retContent); + // 将查询结果解析为json字符串 + + } + /* + * 6.向指定表中插入一条记录 + * + * @param _tableStruct 表结构 + * @param _primaryKey 待插记录的主键 + * @param _fields 各字段值 + * @param _isRepeatable 主键下记录是否可重复 + * + * @return 执行状态码 + */ + function insertOneRecord(Bean storage _tableStruct, string memory _primaryKey, string memory _fields, bool _isRepeatable) internal returns (int8) { + // require(bytes(_primaryKey).length > 0); + // require(_fields.length == _tableStruct.fields.length); + int8 setStatus = INITIAL_STATE; + int8 getStatus = INITIAL_STATE; + string memory getContent; + string[] memory fieldsArray; + Table table = openTable(_tableStruct.tableName); + if(_isRepeatable){ + getStatus = FAIL_NULL_RETURN; + }else{ + (getStatus, getContent) = selectOneRecordToJson(_tableStruct, _primaryKey); + } + if (getStatus == FAIL_NULL_RETURN) { + fieldsArray = getFieldsArray(_fields); + // 创建表记录 + Entry entry = table.newEntry(); + entry.set(_tableStruct.primaryKey, _primaryKey); + for (uint i = 0; i < _tableStruct.fields.length; i++) { + entry.set(_tableStruct.fields[i], fieldsArray[i]); + } + setStatus = FAIL_INSERT; + //新增表记录 + if (table.insert(_primaryKey, entry) == 1) { + setStatus = SUCCESS_RETURN; + } else { + setStatus = FAIL_RETURN; + } + } else { + setStatus = FAIL_ALREADY_EXIST; + } + return setStatus; + } + + /* + * 7.向指定表中更新一条记录 + * + * @param _tableStruct 表结构 + * @param _primaryKey 待更新记录的主键 + * @param _fields 各字段值组成的字符串 + * + * @return 执行状态码 + */ + function updateOneRecord(Bean storage _tableStruct, string memory _primaryKey, string memory _fields) internal returns(int8) { + Table table = openTable(_tableStruct.tableName); + string[] memory fieldsArray = getFieldsArray(_fields); + Entry entry = table.newEntry(); + Condition condition = table.newCondition(); + entry.set(_tableStruct.primaryKey, _primaryKey); + for (uint i = 0; i < _tableStruct.fields.length; i++) { + entry.set(_tableStruct.fields[i], fieldsArray[i]); + } + int count = table.update(_primaryKey, entry, condition); + if(count == 1){ + return SUCCESS_RETURN; + }else{ + return FAIL_RETURN; + } + } + /* + * 8.修改各字段中某一个字段,字符串格式输出 + * + * @param _fields 各字段值的字符串数组 + * @param index 待修改字段的位置 + * @param values 修改后的值 + * + * @return 修改后的各字段值,并以字符串格式输出 + * + */ + function getChangeFieldsString(string[] memory _fields, uint index, string values) public returns (string){ + string[] memory fieldsArray = _fields; + fieldsArray[index] = values; + return StringUtil.strConcatWithComma(fieldsArray); + } +} diff --git a/2021-BeiJing-BSN3/PRG/Contracts/User.sol b/2021-BeiJing-BSN3/PRG/Contracts/User.sol new file mode 100644 index 000000000..46ae790b2 --- /dev/null +++ b/2021-BeiJing-BSN3/PRG/Contracts/User.sol @@ -0,0 +1,191 @@ +pragma solidity ^0.4.25; +pragma experimental ABIEncoderV2; +import "./lib/SafeMath.sol"; +import "./utils/TimeUtil.sol"; +import "./utils/StringUtil.sol"; +import "./utils/TypeConvertUtil.sol"; +import "./TableDefTools.sol"; + +/* +* User实现用户的链上信息管理,本合约主要是针对用户表进行的一系列操作。后面的留言表以及资源表会用到积分管理,因此需要引用本合约 +* 以Table的形式进行存储。主要包含功能有:1.用户注册2.查看用户是否注册3.查看用户信用积分4.用户登陆5.对用户信用积分进行管理 +* 首先看用户是否已经在链上注册信息,若未注册,则可以进行注册。 +* 用户注册实际上是在合约用户信息表中插入一条记录,描述该用户的详细情况。 +* 用户处于已注册状态才会有记录信息,因此,注册后方可使用其他板块功能。 +* +*/ + +contract User is TableDefTools{ + /******* 引入库 *******/ + using TimeUtil for *; + using SafeMath for *; + using TypeConvertUtil for *; + using StringUtil for *; + /* + * 构造函数,初始化使用到的表结构 + * + * @param 无 + * + * @return 无 + */ + constructor() public{ + //初始化需要用到的表。用户信息表 + initTableStruct(t_user_struct, TABLE_USER_NAME, TABLE_USER_PRIMARYKEY, TABLE_USER_FIELDS); + } + // 事件 + event REGISTER_USER_EVENT(string userid,string usertype,string activatetime); //注册用户事件.记录注册人身份,类型,注册时间 + event DEL_CREDITPOINT_EVENT(string user_id,string grade,string time); //扣分时间,扣分人id、扣分数、扣分时间 + event ADD_CREDITPOINT_EVENT(string user_id,string grade,string time); //加分时间,扣分人id、扣分数、扣分时间 + /* + * 1.用户注册 + * + * @param _userid 用户id + * @param _fields 用户信息表各字段值拼接成的字符串(除最后三个字段;用逗号分隔。最后三个字段分别是注册时间【根据注册时间生成】,注册状态 + 注册后这个值默认为1,信用积分默认为100分),包括如下: + * 用户ID(主键) + * 用户名 + * 登陆密码 + * 注册时间 + * 注册状态[1代表已注册,0代表为注册] + * 信用积分 + * + * @return 执行状态码 + * + * 测试举例 参数一:"191867345212322" + * 参数二:"江会文","123456","农民" + *注册成功返回SUCCESS否则返回错误码,错误码对应的问题请参考DB + */ + function activateUser(string memory _userid,string memory _username,string memory _userpassword, string memory _usertype) public returns(int8){ + // 获得当前的时间 + string memory _passwordhash=TypeConvertUtil.bytes32ToString(sha256(abi.encode(_userid,_userpassword))); + string memory nowDate = TimeUtil.getNowDate(); + string memory firstFiveParams=StringUtil.strConcat7(_username,',',_passwordhash,',',nowDate,',',_usertype); + string memory lastTwoParams = "1,100"; + string memory storeFields = StringUtil.strConcat3(firstFiveParams,',',lastTwoParams); + emit REGISTER_USER_EVENT(StringUtil.strConcat2("注册人的ID为:",_userid),StringUtil.strConcat2("注册人身份为:",_usertype),StringUtil.strConcat2("注册时间为:",nowDate)); + return insertOneRecord(t_user_struct,_userid,storeFields,false);//最后的false代表主键下记录不可重复 + } + + /* + * 2.查询用户是否已经注册 + * + * @param _userid 用户id + * + * @return 注册状态,1为已注册,0为未注册 + * + * 测试举例 参数一:"191867345212322" + + */ + function isUserActivated(string memory _userid) public view returns (string) { + int8 retCode; + string[] memory retArray; + (retCode, retArray) = getUserRecordArray(_userid); + return retArray[4]; + } + /* + * 3.查询用户的身份类别 + * + * @param _userid 用户id + * + * @return 用户身份类别 + * + * 测试举例 参数一:"191867345212322" + */ + function getUserType(string _userid) public view returns (string) { + int8 retCode; + string[] memory retArray; + (retCode, retArray) = getUserRecordArray(_userid); + return retArray[3]; + } + + /* + * 4.查询用户信用积分。返回uint类型 + * + * @param _userid 用户id + * + * @return 用户信用积分值 + * + * 测试举例 参数一:"191867345212322" + */ + function getUserCreditNum(string _userid) public view returns (uint) { + int8 retCode; + uint UserCreditNum; + string[] memory retArray; + (retCode, retArray) = getUserRecordArray(_userid); + if(retCode == SUCCESS_RETURN){ + UserCreditNum=TypeConvertUtil.stringToUint(retArray[5]); + return UserCreditNum; + }else{ + return uint(-1); + } + } + /* + * 5.用户登陆 + * + * @param userid 用户id + * @param userpassword 用户密码 + * @return 用户所有信息并以JSON格式返回 + * + * 测试举例 参数一:"191867345212322,123456" + */ + function Login(string memory _userid,string memory _userpassword) public view returns (int8,string) { + string memory _passwordhash=TypeConvertUtil.bytes32ToString(sha256(abi.encode(_userid,_userpassword))); + return loginInToJson(t_user_struct,_userid,_passwordhash); + } + /* + * 6.查询用户信息并以字符串数组方式输出 + * + * @param _userid 用户id + * + * @return 执行状态码 + * @return 该用户信息的字符串数组 + * + * 测试举例 参数一:"191867345212322" + */ + function getUserRecordArray(string userid) public view returns(int8, string[]){ + return selectOneRecordToArray(t_user_struct, userid, ["user_id",userid]); + } + /* + * 7.向用户表进行积分管理操作 + * + * @param _primaryKey 用户ID,唯一主键 + * @param _integral 需要进行增加/扣除的分值 + * @param _opcode 操作符:为1加分,为0扣分.根据用户发言好坏决定它是否会被扣分 + * @return 执行状态码 + *测试举例 参数一:"191867345212322",5,1 + */ + function updateUserIntegral(string memory _userid, uint _integral,int8 _opcode) public returns(int8) { + // 该用户当前信用积分值 + uint userHasIntegral; + // 该用户更新后的信用积分值 + uint userNowIntegral; + // 查询用户信息返回状态 + int8 queryRetCode; + // 更新用户信息返回状态 + int8 updateRetCode; + // 数据表返回信息 + string[] memory retArray; + // 获得当前的时间 + string memory updateTime= TimeUtil.getNowDate(); + // 查看该用户记录信息 + (queryRetCode, retArray) = selectOneRecordToArray(t_user_struct, _userid, ["user_id", _userid]); + //该用户存在 + //将用户信用积分转换为整数进行操作 + userHasIntegral =getUserCreditNum(_userid); + // 更新用户信用积分,对积分进行对应加分/减分操作。 + if(_opcode==0){ //为0 扣分 + userNowIntegral=SafeMath.sub(userHasIntegral,_integral);//调用Safemath的减法操作,避免溢出问题 + //记录日志 + emit DEL_CREDITPOINT_EVENT(StringUtil.strConcat2("扣分人的ID为:",_userid),StringUtil.strConcat2("当前信用积分值:",TypeConvertUtil.uintToString(userNowIntegral)),StringUtil.strConcat2("信用积分更新时间为:",updateTime)); + }else{ //加分 + userNowIntegral=SafeMath.add(userHasIntegral,_integral); + + emit ADD_CREDITPOINT_EVENT(StringUtil.strConcat2("加分人的ID为:",_userid),StringUtil.strConcat2("当前信用积分值:",TypeConvertUtil.uintToString(userNowIntegral)),StringUtil.strConcat2("信用积分更新时间为:",updateTime)); + } + //对信用分操作完毕后。更新用户表 + string memory changedFieldsStr = getChangeFieldsString(retArray,5, TypeConvertUtil.uintToString(userNowIntegral));//转换回字符串类型 + return(updateOneRecord(t_user_struct, _userid, changedFieldsStr)); + } +} + + diff --git a/2021-BeiJing-BSN3/PRG/Contracts/lib/DateTimeLibrary.sol b/2021-BeiJing-BSN3/PRG/Contracts/lib/DateTimeLibrary.sol new file mode 100644 index 000000000..bcc5d59d4 --- /dev/null +++ b/2021-BeiJing-BSN3/PRG/Contracts/lib/DateTimeLibrary.sol @@ -0,0 +1,300 @@ +pragma solidity ^0.4.25; + +// ---------------------------------------------------------------------------- +// BokkyPooBah's DateTime Library v1.01 +// +// A gas-efficient Solidity date and time library +// +// https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary +// +// Tested date range 1970/01/01 to 2345/12/31 +// +// Conventions: +// Unit | Range | Notes +// :-------- |:-------------:|:----- +// timestamp | >= 0 | Unix timestamp, number of seconds since 1970/01/01 00:00:00 UTC +// year | 1970 ... 2345 | +// month | 1 ... 12 | +// day | 1 ... 31 | +// hour | 0 ... 23 | +// minute | 0 ... 59 | +// second | 0 ... 59 | +// dayOfWeek | 1 ... 7 | 1 = Monday, ..., 7 = Sunday +// +// +// Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2018-2019. The MIT Licence. +// ---------------------------------------------------------------------------- + +library DateTimeLibrary { + + uint constant SECONDS_PER_DAY = 24 * 60 * 60; + uint constant SECONDS_PER_HOUR = 60 * 60; + uint constant SECONDS_PER_MINUTE = 60; + int constant OFFSET19700101 = 2440588; + + uint constant DOW_MON = 1; + uint constant DOW_TUE = 2; + uint constant DOW_WED = 3; + uint constant DOW_THU = 4; + uint constant DOW_FRI = 5; + uint constant DOW_SAT = 6; + uint constant DOW_SUN = 7; + + // ------------------------------------------------------------------------ + // Calculate the number of days from 1970/01/01 to year/month/day using + // the date conversion algorithm from + // http://aa.usno.navy.mil/faq/docs/JD_Formula.php + // and subtracting the offset 2440588 so that 1970/01/01 is day 0 + // + // days = day + // - 32075 + // + 1461 * (year + 4800 + (month - 14) / 12) / 4 + // + 367 * (month - 2 - (month - 14) / 12 * 12) / 12 + // - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4 + // - offset + // ------------------------------------------------------------------------ + function _daysFromDate(uint year, uint month, uint day) internal pure returns (uint _days) { + require(year >= 1970); + int _year = int(year); + int _month = int(month); + int _day = int(day); + + int __days = _day + - 32075 + + 1461 * (_year + 4800 + (_month - 14) / 12) / 4 + + 367 * (_month - 2 - (_month - 14) / 12 * 12) / 12 + - 3 * ((_year + 4900 + (_month - 14) / 12) / 100) / 4 + - OFFSET19700101; + + _days = uint(__days); + } + + // ------------------------------------------------------------------------ + // Calculate year/month/day from the number of days since 1970/01/01 using + // the date conversion algorithm from + // http://aa.usno.navy.mil/faq/docs/JD_Formula.php + // and adding the offset 2440588 so that 1970/01/01 is day 0 + // + // int L = days + 68569 + offset + // int N = 4 * L / 146097 + // L = L - (146097 * N + 3) / 4 + // year = 4000 * (L + 1) / 1461001 + // L = L - 1461 * year / 4 + 31 + // month = 80 * L / 2447 + // dd = L - 2447 * month / 80 + // L = month / 11 + // month = month + 2 - 12 * L + // year = 100 * (N - 49) + year + L + // ------------------------------------------------------------------------ + function _daysToDate(uint _days) internal pure returns (uint year, uint month, uint day) { + int __days = int(_days); + + int L = __days + 68569 + OFFSET19700101; + int N = 4 * L / 146097; + L = L - (146097 * N + 3) / 4; + int _year = 4000 * (L + 1) / 1461001; + L = L - 1461 * _year / 4 + 31; + int _month = 80 * L / 2447; + int _day = L - 2447 * _month / 80; + L = _month / 11; + _month = _month + 2 - 12 * L; + _year = 100 * (N - 49) + _year + L; + + year = uint(_year); + month = uint(_month); + day = uint(_day); + } + + function timestampFromDate(uint year, uint month, uint day) internal pure returns (uint timestamp) { + timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY; + } + function timestampFromDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (uint timestamp) { + timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + hour * SECONDS_PER_HOUR + minute * SECONDS_PER_MINUTE + second; + } + function timestampToDate(uint timestamp) internal pure returns (uint year, uint month, uint day) { + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + } + function timestampToDateTime(uint timestamp) internal pure returns (uint year, uint month, uint day, uint hour, uint minute, uint second) { + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + uint secs = timestamp % SECONDS_PER_DAY; + hour = secs / SECONDS_PER_HOUR; + secs = secs % SECONDS_PER_HOUR; + minute = secs / SECONDS_PER_MINUTE; + second = secs % SECONDS_PER_MINUTE; + } + + function isValidDate(uint year, uint month, uint day) internal pure returns (bool valid) { + if (year >= 1970 && month > 0 && month <= 12) { + uint daysInMonth = _getDaysInMonth(year, month); + if (day > 0 && day <= daysInMonth) { + valid = true; + } + } + } + function isValidDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (bool valid) { + if (isValidDate(year, month, day)) { + if (hour < 24 && minute < 60 && second < 60) { + valid = true; + } + } + } + function isLeapYear(uint timestamp) internal pure returns (bool leapYear) { + (uint year,,) = _daysToDate(timestamp / SECONDS_PER_DAY); + leapYear = _isLeapYear(year); + } + function _isLeapYear(uint year) internal pure returns (bool leapYear) { + leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); + } + function isWeekDay(uint timestamp) internal pure returns (bool weekDay) { + weekDay = getDayOfWeek(timestamp) <= DOW_FRI; + } + function isWeekEnd(uint timestamp) internal pure returns (bool weekEnd) { + weekEnd = getDayOfWeek(timestamp) >= DOW_SAT; + } + function getDaysInMonth(uint timestamp) internal pure returns (uint daysInMonth) { + (uint year, uint month,) = _daysToDate(timestamp / SECONDS_PER_DAY); + daysInMonth = _getDaysInMonth(year, month); + } + function _getDaysInMonth(uint year, uint month) internal pure returns (uint daysInMonth) { + if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) { + daysInMonth = 31; + } else if (month != 2) { + daysInMonth = 30; + } else { + daysInMonth = _isLeapYear(year) ? 29 : 28; + } + } + // 1 = Monday, 7 = Sunday + function getDayOfWeek(uint timestamp) internal pure returns (uint dayOfWeek) { + uint _days = timestamp / SECONDS_PER_DAY; + dayOfWeek = (_days + 3) % 7 + 1; + } + + function getYear(uint timestamp) internal pure returns (uint year) { + (year,,) = _daysToDate(timestamp / SECONDS_PER_DAY); + } + function getMonth(uint timestamp) internal pure returns (uint month) { + (,month,) = _daysToDate(timestamp / SECONDS_PER_DAY); + } + function getDay(uint timestamp) internal pure returns (uint day) { + (,,day) = _daysToDate(timestamp / SECONDS_PER_DAY); + } + function getHour(uint timestamp) internal pure returns (uint hour) { + uint secs = timestamp % SECONDS_PER_DAY; + hour = secs / SECONDS_PER_HOUR; + } + function getMinute(uint timestamp) internal pure returns (uint minute) { + uint secs = timestamp % SECONDS_PER_HOUR; + minute = secs / SECONDS_PER_MINUTE; + } + function getSecond(uint timestamp) internal pure returns (uint second) { + second = timestamp % SECONDS_PER_MINUTE; + } + + function addYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) { + (uint year, uint month, uint day) = _daysToDate(timestamp / SECONDS_PER_DAY); + year += _years; + uint daysInMonth = _getDaysInMonth(year, month); + if (day > daysInMonth) { + day = daysInMonth; + } + newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; + require(newTimestamp >= timestamp); + } + function addMonths(uint timestamp, uint _months) internal pure returns (uint newTimestamp) { + (uint year, uint month, uint day) = _daysToDate(timestamp / SECONDS_PER_DAY); + month += _months; + year += (month - 1) / 12; + month = (month - 1) % 12 + 1; + uint daysInMonth = _getDaysInMonth(year, month); + if (day > daysInMonth) { + day = daysInMonth; + } + newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; + require(newTimestamp >= timestamp); + } + function addDays(uint timestamp, uint _days) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp + _days * SECONDS_PER_DAY; + require(newTimestamp >= timestamp); + } + function addHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp + _hours * SECONDS_PER_HOUR; + require(newTimestamp >= timestamp); + } + function addMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp + _minutes * SECONDS_PER_MINUTE; + require(newTimestamp >= timestamp); + } + function addSeconds(uint timestamp, uint _seconds) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp + _seconds; + require(newTimestamp >= timestamp); + } + + function subYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) { + (uint year, uint month, uint day) = _daysToDate(timestamp / SECONDS_PER_DAY); + year -= _years; + uint daysInMonth = _getDaysInMonth(year, month); + if (day > daysInMonth) { + day = daysInMonth; + } + newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; + require(newTimestamp <= timestamp); + } + function subMonths(uint timestamp, uint _months) internal pure returns (uint newTimestamp) { + (uint year, uint month, uint day) = _daysToDate(timestamp / SECONDS_PER_DAY); + uint yearMonth = year * 12 + (month - 1) - _months; + year = yearMonth / 12; + month = yearMonth % 12 + 1; + uint daysInMonth = _getDaysInMonth(year, month); + if (day > daysInMonth) { + day = daysInMonth; + } + newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; + require(newTimestamp <= timestamp); + } + function subDays(uint timestamp, uint _days) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp - _days * SECONDS_PER_DAY; + require(newTimestamp <= timestamp); + } + function subHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp - _hours * SECONDS_PER_HOUR; + require(newTimestamp <= timestamp); + } + function subMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp - _minutes * SECONDS_PER_MINUTE; + require(newTimestamp <= timestamp); + } + function subSeconds(uint timestamp, uint _seconds) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp - _seconds; + require(newTimestamp <= timestamp); + } + + function diffYears(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _years) { + require(fromTimestamp <= toTimestamp); + (uint fromYear,,) = _daysToDate(fromTimestamp / SECONDS_PER_DAY); + (uint toYear,,) = _daysToDate(toTimestamp / SECONDS_PER_DAY); + _years = toYear - fromYear; + } + function diffMonths(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _months) { + require(fromTimestamp <= toTimestamp); + (uint fromYear, uint fromMonth,) = _daysToDate(fromTimestamp / SECONDS_PER_DAY); + (uint toYear, uint toMonth,) = _daysToDate(toTimestamp / SECONDS_PER_DAY); + _months = toYear * 12 + toMonth - fromYear * 12 - fromMonth; + } + function diffDays(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _days) { + require(fromTimestamp <= toTimestamp); + _days = (toTimestamp - fromTimestamp) / SECONDS_PER_DAY; + } + function diffHours(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _hours) { + require(fromTimestamp <= toTimestamp); + _hours = (toTimestamp - fromTimestamp) / SECONDS_PER_HOUR; + } + function diffMinutes(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _minutes) { + require(fromTimestamp <= toTimestamp); + _minutes = (toTimestamp - fromTimestamp) / SECONDS_PER_MINUTE; + } + function diffSeconds(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _seconds) { + require(fromTimestamp <= toTimestamp); + _seconds = toTimestamp - fromTimestamp; + } +} \ No newline at end of file diff --git a/2021-BeiJing-BSN3/PRG/Contracts/lib/SafeMath.sol b/2021-BeiJing-BSN3/PRG/Contracts/lib/SafeMath.sol new file mode 100644 index 000000000..554fb5a82 --- /dev/null +++ b/2021-BeiJing-BSN3/PRG/Contracts/lib/SafeMath.sol @@ -0,0 +1,106 @@ +pragma solidity ^0.4.25; + +/** + * @dev Wrappers over Solidity's arithmetic operations with added overflow + * checks. + * + * Arithmetic operations in Solidity wrap on overflow. This can easily result + * in bugs, because programmers usually assume that an overflow raises an + * error, which is the standard behavior in high level programming languages. + * `SafeMath` restores this intuition by reverting the transaction when an + * operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + */ +library SafeMath { + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * - Addition cannot overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a, "SafeMath: addition overflow"); + return c; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + require(b <= a, "SafeMath: subtraction overflow"); + uint256 c = a - b; + + return c; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * - Multiplication cannot overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 + if (a == 0) { + return 0; + } + + uint256 c = a * b; + require(c / a == b, "SafeMath: multiplication overflow"); + + return c; + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + // Solidity only automatically asserts when dividing by 0 + require(b > 0, "SafeMath: division by zero"); + uint256 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + require(b != 0, "SafeMath: modulo by zero"); + return a % b; + } +} diff --git a/2021-BeiJing-BSN3/PRG/Contracts/lib/Strings.sol b/2021-BeiJing-BSN3/PRG/Contracts/lib/Strings.sol new file mode 100644 index 000000000..4b659fec4 --- /dev/null +++ b/2021-BeiJing-BSN3/PRG/Contracts/lib/Strings.sol @@ -0,0 +1,717 @@ +/* + * @title String & slice utility library for Solidity contracts. + * @author Nick Johnson + * + * @dev Functionality in this library is largely implemented using an + * abstraction called a 'slice'. A slice represents a part of a string - + * anything from the entire string to a single character, or even no + * characters at all (a 0-length slice). Since a slice only has to specify + * an offset and a length, copying and manipulating slices is a lot less + * expensive than copying and manipulating the strings they reference. + * + * To further reduce gas costs, most functions on slice that need to return + * a slice modify the original one instead of allocating a new one; for + * instance, `s.split(".")` will return the text up to the first '.', + * modifying s to only contain the remainder of the string after the '.'. + * In situations where you do not want to modify the original slice, you + * can make a copy first with `.copy()`, for example: + * `s.copy().split(".")`. Try and avoid using this idiom in loops; since + * Solidity has no memory management, it will result in allocating many + * short-lived slices that are later discarded. + * + * Functions that return two slices come in two versions: a non-allocating + * version that takes the second slice as an argument, modifying it in + * place, and an allocating version that allocates and returns the second + * slice; see `nextRune` for example. + * + * Functions that have to copy string data will return strings rather than + * slices; these can be cast back to slices for further processing if + * required. + * + * For convenience, some functions are provided with non-modifying + * variants that create a new slice and return both; for instance, + * `s.splitNew('.')` leaves s unmodified, and returns two values + * corresponding to the left and right parts of the string. + */ + +pragma solidity ^0.4.25; + +library Strings { + struct slice { + uint _len; + uint _ptr; + } + + function memcpy(uint dest, uint src, uint len) private pure { + // Copy word-length chunks while possible + for(; len >= 32; len -= 32) { + assembly { + mstore(dest, mload(src)) + } + dest += 32; + src += 32; + } + + // Copy remaining bytes + uint mask = 256 ** (32 - len) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) + let destpart := and(mload(dest), mask) + mstore(dest, or(destpart, srcpart)) + } + } + + /* + * @dev Returns a slice containing the entire string. + * @param self The string to make a slice from. + * @return A newly allocated slice containing the entire string. + */ + function toSlice(string memory self) internal pure returns (slice memory) { + uint ptr; + assembly { + ptr := add(self, 0x20) + } + return slice(bytes(self).length, ptr); + } + + + /* + * @dev Returns the length of a null-terminated bytes32 string. + * @param self The value to find the length of. + * @return The length of the string, from 0 to 32. + */ + function len(bytes32 self) internal pure returns (uint) { + uint ret; + if (self == 0) + return 0; + if (self & 0xffffffffffffffffffffffffffffffff == 0) { + ret += 16; + self = bytes32(uint(self) / 0x100000000000000000000000000000000); + } + if (self & 0xffffffffffffffff == 0) { + ret += 8; + self = bytes32(uint(self) / 0x10000000000000000); + } + if (self & 0xffffffff == 0) { + ret += 4; + self = bytes32(uint(self) / 0x100000000); + } + if (self & 0xffff == 0) { + ret += 2; + self = bytes32(uint(self) / 0x10000); + } + if (self & 0xff == 0) { + ret += 1; + } + return 32 - ret; + } + + /* + * @dev Returns a slice containing the entire bytes32, interpreted as a + * null-terminated utf-8 string. + * @param self The bytes32 value to convert to a slice. + * @return A new slice containing the value of the input argument up to the + * first null. + */ + function toSliceB32(bytes32 self) internal pure returns (slice memory ret) { + // Allocate space for `self` in memory, copy it there, and point ret at it + assembly { + let ptr := mload(0x40) + mstore(0x40, add(ptr, 0x20)) + mstore(ptr, self) + mstore(add(ret, 0x20), ptr) + } + ret._len = len(self); + } + + /* + * @dev Returns a new slice containing the same data as the current slice. + * @param self The slice to copy. + * @return A new slice containing the same data as `self`. + */ + function copy(slice memory self) internal pure returns (slice memory) { + return slice(self._len, self._ptr); + } + + /* + * @dev Copies a slice to a new string. + * @param self The slice to copy. + * @return A newly allocated string containing the slice's text. + */ + function toString(slice memory self) internal pure returns (string memory) { + string memory ret = new string(self._len); + uint retptr; + assembly { retptr := add(ret, 32) } + + memcpy(retptr, self._ptr, self._len); + return ret; + } + + /* + * @dev Returns the length in runes of the slice. Note that this operation + * takes time proportional to the length of the slice; avoid using it + * in loops, and call `slice.empty()` if you only need to know whether + * the slice is empty or not. + * @param self The slice to operate on. + * @return The length of the slice in runes. + */ + function len(slice memory self) internal pure returns (uint l) { + // Starting at ptr-31 means the LSB will be the byte we care about + uint ptr = self._ptr - 31; + uint end = ptr + self._len; + for (l = 0; ptr < end; l++) { + uint8 b; + assembly { b := and(mload(ptr), 0xFF) } + if (b < 0x80) { + ptr += 1; + } else if(b < 0xE0) { + ptr += 2; + } else if(b < 0xF0) { + ptr += 3; + } else if(b < 0xF8) { + ptr += 4; + } else if(b < 0xFC) { + ptr += 5; + } else { + ptr += 6; + } + } + } + + /* + * @dev Returns true if the slice is empty (has a length of 0). + * @param self The slice to operate on. + * @return True if the slice is empty, False otherwise. + */ + function empty(slice memory self) internal pure returns (bool) { + return self._len == 0; + } + + /* + * @dev Returns a positive number if `other` comes lexicographically after + * `self`, a negative number if it comes before, or zero if the + * contents of the two slices are equal. Comparison is done per-rune, + * on unicode codepoints. + * @param self The first slice to compare. + * @param other The second slice to compare. + * @return The result of the comparison. + */ + function compare(slice memory self, slice memory other) internal pure returns (int) { + uint shortest = self._len; + if (other._len < self._len) + shortest = other._len; + + uint selfptr = self._ptr; + uint otherptr = other._ptr; + for (uint idx = 0; idx < shortest; idx += 32) { + uint a; + uint b; + assembly { + a := mload(selfptr) + b := mload(otherptr) + } + if (a != b) { + // Mask out irrelevant bytes and check again + uint256 mask = uint256(-1); // 0xffff... + if(shortest < 32) { + mask = ~(2 ** (8 * (32 - shortest + idx)) - 1); + } + uint256 diff = (a & mask) - (b & mask); + if (diff != 0) + return int(diff); + } + selfptr += 32; + otherptr += 32; + } + return int(self._len) - int(other._len); + } + + /* + * @dev Returns true if the two slices contain the same text. + * @param self The first slice to compare. + * @param self The second slice to compare. + * @return True if the slices are equal, false otherwise. + */ + function equals(slice memory self, slice memory other) internal pure returns (bool) { + return compare(self, other) == 0; + } + + /* + * @dev Extracts the first rune in the slice into `rune`, advancing the + * slice to point to the next rune and returning `self`. + * @param self The slice to operate on. + * @param rune The slice that will contain the first rune. + * @return `rune`. + */ + function nextRune(slice memory self, slice memory rune) internal pure returns (slice memory) { + rune._ptr = self._ptr; + + if (self._len == 0) { + rune._len = 0; + return rune; + } + + uint l; + uint b; + // Load the first byte of the rune into the LSBs of b + assembly { b := and(mload(sub(mload(add(self, 32)), 31)), 0xFF) } + if (b < 0x80) { + l = 1; + } else if(b < 0xE0) { + l = 2; + } else if(b < 0xF0) { + l = 3; + } else { + l = 4; + } + + // Check for truncated codepoints + if (l > self._len) { + rune._len = self._len; + self._ptr += self._len; + self._len = 0; + return rune; + } + + self._ptr += l; + self._len -= l; + rune._len = l; + return rune; + } + + /* + * @dev Returns the first rune in the slice, advancing the slice to point + * to the next rune. + * @param self The slice to operate on. + * @return A slice containing only the first rune from `self`. + */ + function nextRune(slice memory self) internal pure returns (slice memory ret) { + nextRune(self, ret); + } + + /* + * @dev Returns the number of the first codepoint in the slice. + * @param self The slice to operate on. + * @return The number of the first codepoint in the slice. + */ + function ord(slice memory self) internal pure returns (uint ret) { + if (self._len == 0) { + return 0; + } + + uint word; + uint length; + uint divisor = 2 ** 248; + + // Load the rune into the MSBs of b + assembly { word:= mload(mload(add(self, 32))) } + uint b = word / divisor; + if (b < 0x80) { + ret = b; + length = 1; + } else if(b < 0xE0) { + ret = b & 0x1F; + length = 2; + } else if(b < 0xF0) { + ret = b & 0x0F; + length = 3; + } else { + ret = b & 0x07; + length = 4; + } + + // Check for truncated codepoints + if (length > self._len) { + return 0; + } + + for (uint i = 1; i < length; i++) { + divisor = divisor / 256; + b = (word / divisor) & 0xFF; + if (b & 0xC0 != 0x80) { + // Invalid UTF-8 sequence + return 0; + } + ret = (ret * 64) | (b & 0x3F); + } + + return ret; + } + + /* + * @dev Returns the keccak-256 hash of the slice. + * @param self The slice to hash. + * @return The hash of the slice. + */ + function keccak(slice memory self) internal pure returns (bytes32 ret) { + assembly { + ret := keccak256(mload(add(self, 32)), mload(self)) + } + } + + /* + * @dev Returns true if `self` starts with `needle`. + * @param self The slice to operate on. + * @param needle The slice to search for. + * @return True if the slice starts with the provided text, false otherwise. + */ + function startsWith(slice memory self, slice memory needle) internal pure returns (bool) { + if (self._len < needle._len) { + return false; + } + + if (self._ptr == needle._ptr) { + return true; + } + + bool equal; + assembly { + let length := mload(needle) + let selfptr := mload(add(self, 0x20)) + let needleptr := mload(add(needle, 0x20)) + equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) + } + return equal; + } + + /* + * @dev If `self` starts with `needle`, `needle` is removed from the + * beginning of `self`. Otherwise, `self` is unmodified. + * @param self The slice to operate on. + * @param needle The slice to search for. + * @return `self` + */ + function beyond(slice memory self, slice memory needle) internal pure returns (slice memory) { + if (self._len < needle._len) { + return self; + } + + bool equal = true; + if (self._ptr != needle._ptr) { + assembly { + let length := mload(needle) + let selfptr := mload(add(self, 0x20)) + let needleptr := mload(add(needle, 0x20)) + equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) + } + } + + if (equal) { + self._len -= needle._len; + self._ptr += needle._len; + } + + return self; + } + + /* + * @dev Returns true if the slice ends with `needle`. + * @param self The slice to operate on. + * @param needle The slice to search for. + * @return True if the slice starts with the provided text, false otherwise. + */ + function endsWith(slice memory self, slice memory needle) internal pure returns (bool) { + if (self._len < needle._len) { + return false; + } + + uint selfptr = self._ptr + self._len - needle._len; + + if (selfptr == needle._ptr) { + return true; + } + + bool equal; + assembly { + let length := mload(needle) + let needleptr := mload(add(needle, 0x20)) + equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) + } + + return equal; + } + + /* + * @dev If `self` ends with `needle`, `needle` is removed from the + * end of `self`. Otherwise, `self` is unmodified. + * @param self The slice to operate on. + * @param needle The slice to search for. + * @return `self` + */ + function until(slice memory self, slice memory needle) internal pure returns (slice memory) { + if (self._len < needle._len) { + return self; + } + + uint selfptr = self._ptr + self._len - needle._len; + bool equal = true; + if (selfptr != needle._ptr) { + assembly { + let length := mload(needle) + let needleptr := mload(add(needle, 0x20)) + equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) + } + } + + if (equal) { + self._len -= needle._len; + } + + return self; + } + + // Returns the memory address of the first byte of the first occurrence of + // `needle` in `self`, or the first byte after `self` if not found. + function findPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private pure returns (uint) { + uint ptr = selfptr; + uint idx; + + if (needlelen <= selflen) { + if (needlelen <= 32) { + bytes32 mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1)); + + bytes32 needledata; + assembly { needledata := and(mload(needleptr), mask) } + + uint end = selfptr + selflen - needlelen; + bytes32 ptrdata; + assembly { ptrdata := and(mload(ptr), mask) } + + while (ptrdata != needledata) { + if (ptr >= end) + return selfptr + selflen; + ptr++; + assembly { ptrdata := and(mload(ptr), mask) } + } + return ptr; + } else { + // For long needles, use hashing + bytes32 hash; + assembly { hash := keccak256(needleptr, needlelen) } + + for (idx = 0; idx <= selflen - needlelen; idx++) { + bytes32 testHash; + assembly { testHash := keccak256(ptr, needlelen) } + if (hash == testHash) + return ptr; + ptr += 1; + } + } + } + return selfptr + selflen; + } + + // Returns the memory address of the first byte after the last occurrence of + // `needle` in `self`, or the address of `self` if not found. + function rfindPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private pure returns (uint) { + uint ptr; + + if (needlelen <= selflen) { + if (needlelen <= 32) { + bytes32 mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1)); + + bytes32 needledata; + assembly { needledata := and(mload(needleptr), mask) } + + ptr = selfptr + selflen - needlelen; + bytes32 ptrdata; + assembly { ptrdata := and(mload(ptr), mask) } + + while (ptrdata != needledata) { + if (ptr <= selfptr) + return selfptr; + ptr--; + assembly { ptrdata := and(mload(ptr), mask) } + } + return ptr + needlelen; + } else { + // For long needles, use hashing + bytes32 hash; + assembly { hash := keccak256(needleptr, needlelen) } + ptr = selfptr + (selflen - needlelen); + while (ptr >= selfptr) { + bytes32 testHash; + assembly { testHash := keccak256(ptr, needlelen) } + if (hash == testHash) + return ptr + needlelen; + ptr -= 1; + } + } + } + return selfptr; + } + + /* + * @dev Modifies `self` to contain everything from the first occurrence of + * `needle` to the end of the slice. `self` is set to the empty slice + * if `needle` is not found. + * @param self The slice to search and modify. + * @param needle The text to search for. + * @return `self`. + */ + function find(slice memory self, slice memory needle) internal pure returns (slice memory) { + uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); + self._len -= ptr - self._ptr; + self._ptr = ptr; + return self; + } + + /* + * @dev Modifies `self` to contain the part of the string from the start of + * `self` to the end of the first occurrence of `needle`. If `needle` + * is not found, `self` is set to the empty slice. + * @param self The slice to search and modify. + * @param needle The text to search for. + * @return `self`. + */ + function rfind(slice memory self, slice memory needle) internal pure returns (slice memory) { + uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); + self._len = ptr - self._ptr; + return self; + } + + /* + * @dev Splits the slice, setting `self` to everything after the first + * occurrence of `needle`, and `token` to everything before it. If + * `needle` does not occur in `self`, `self` is set to the empty slice, + * and `token` is set to the entirety of `self`. + * @param self The slice to split. + * @param needle The text to search for in `self`. + * @param token An output parameter to which the first token is written. + * @return `token`. + */ + function split(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) { + uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); + token._ptr = self._ptr; + token._len = ptr - self._ptr; + if (ptr == self._ptr + self._len) { + // Not found + self._len = 0; + } else { + self._len -= token._len + needle._len; + self._ptr = ptr + needle._len; + } + return token; + } + + /* + * @dev Splits the slice, setting `self` to everything after the first + * occurrence of `needle`, and returning everything before it. If + * `needle` does not occur in `self`, `self` is set to the empty slice, + * and the entirety of `self` is returned. + * @param self The slice to split. + * @param needle The text to search for in `self`. + * @return The part of `self` up to the first occurrence of `delim`. + */ + function split(slice memory self, slice memory needle) internal pure returns (slice memory token) { + split(self, needle, token); + } + + /* + * @dev Splits the slice, setting `self` to everything before the last + * occurrence of `needle`, and `token` to everything after it. If + * `needle` does not occur in `self`, `self` is set to the empty slice, + * and `token` is set to the entirety of `self`. + * @param self The slice to split. + * @param needle The text to search for in `self`. + * @param token An output parameter to which the first token is written. + * @return `token`. + */ + function rsplit(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) { + uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); + token._ptr = ptr; + token._len = self._len - (ptr - self._ptr); + if (ptr == self._ptr) { + // Not found + self._len = 0; + } else { + self._len -= token._len + needle._len; + } + return token; + } + + /* + * @dev Splits the slice, setting `self` to everything before the last + * occurrence of `needle`, and returning everything after it. If + * `needle` does not occur in `self`, `self` is set to the empty slice, + * and the entirety of `self` is returned. + * @param self The slice to split. + * @param needle The text to search for in `self`. + * @return The part of `self` after the last occurrence of `delim`. + */ + function rsplit(slice memory self, slice memory needle) internal pure returns (slice memory token) { + rsplit(self, needle, token); + } + + /* + * @dev Counts the number of nonoverlapping occurrences of `needle` in `self`. + * @param self The slice to search. + * @param needle The text to search for in `self`. + * @return The number of occurrences of `needle` found in `self`. + */ + function count(slice memory self, slice memory needle) internal pure returns (uint cnt) { + uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr) + needle._len; + while (ptr <= self._ptr + self._len) { + cnt++; + ptr = findPtr(self._len - (ptr - self._ptr), ptr, needle._len, needle._ptr) + needle._len; + } + } + + /* + * @dev Returns True if `self` contains `needle`. + * @param self The slice to search. + * @param needle The text to search for in `self`. + * @return True if `needle` is found in `self`, false otherwise. + */ + function contains(slice memory self, slice memory needle) internal pure returns (bool) { + return rfindPtr(self._len, self._ptr, needle._len, needle._ptr) != self._ptr; + } + + /* + * @dev Returns a newly allocated string containing the concatenation of + * `self` and `other`. + * @param self The first slice to concatenate. + * @param other The second slice to concatenate. + * @return The concatenation of the two strings. + */ + function concat(slice memory self, slice memory other) internal pure returns (string memory) { + string memory ret = new string(self._len + other._len); + uint retptr; + assembly { retptr := add(ret, 32) } + memcpy(retptr, self._ptr, self._len); + memcpy(retptr + self._len, other._ptr, other._len); + return ret; + } + + /* + * @dev Joins an array of slices, using `self` as a delimiter, returning a + * newly allocated string. + * @param self The delimiter to use. + * @param parts A list of slices to join. + * @return A newly allocated string containing all the slices in `parts`, + * joined with `self`. + */ + function join(slice memory self, slice[] memory parts) internal pure returns (string memory) { + if (parts.length == 0) + return ""; + + uint length = self._len * (parts.length - 1); + for(uint i = 0; i < parts.length; i++) + length += parts[i]._len; + + string memory ret = new string(length); + uint retptr; + assembly { retptr := add(ret, 32) } + + for(i = 0; i < parts.length; i++) { + memcpy(retptr, parts[i]._ptr, parts[i]._len); + retptr += parts[i]._len; + if (i < parts.length - 1) { + memcpy(retptr, self._ptr, self._len); + retptr += self._len; + } + } + + return ret; + } +} diff --git a/2021-BeiJing-BSN3/PRG/Contracts/lib/Table.sol b/2021-BeiJing-BSN3/PRG/Contracts/lib/Table.sol new file mode 100644 index 000000000..814eab06d --- /dev/null +++ b/2021-BeiJing-BSN3/PRG/Contracts/lib/Table.sol @@ -0,0 +1,56 @@ +pragma solidity ^0.4.25; +contract TableFactory { + function openTable(string) public constant returns (Table); //open table + function createTable(string,string,string) public constant returns(int); //create table +} + +//select condition +contract Condition { + function EQ(string, int); + function EQ(string, string); + + function NE(string, int); + function NE(string, string); + + function GT(string, int); + function GE(string, int); + + function LT(string, int); + function LE(string, int); + + function limit(int); + function limit(int, int); +} + +//one record +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; +} + +//record sets +contract Entries { + function get(int) public constant returns(Entry); + function size() public constant returns(int); +} + +//Table main contract +contract Table { + //select api + function select(string, Condition) public constant returns(Entries); + //insert api + function insert(string, Entry) public returns(int); + //update api + function update(string, Entry, Condition) public returns(int); + //remove api + function remove(string, Condition) public returns(int); + + function newEntry() public constant returns(Entry); + function newCondition() public constant returns(Condition); +} \ No newline at end of file diff --git a/2021-BeiJing-BSN3/PRG/Contracts/proposal.sol b/2021-BeiJing-BSN3/PRG/Contracts/proposal.sol new file mode 100644 index 000000000..f7740fa35 --- /dev/null +++ b/2021-BeiJing-BSN3/PRG/Contracts/proposal.sol @@ -0,0 +1,252 @@ +pragma solidity ^0.4.25; +pragma experimental ABIEncoderV2; +import "./lib/SafeMath.sol"; +import "./utils/TimeUtil.sol"; +import "./TableDefTools.sol"; + +/* +* +* proposal实现用户提议功能。针对用户提议内容会调用一次文本审核api接口,若存在违规词将会给用户扣分 +* 首先查看用户是否注册,登陆。如果没有则不能提议 +* 操作的表为t_proposal. +*user_id,proposal_content,proposal_time,approval_cnt,disapproval_cnt,check_result +*/ +contract proposal is TableDefTools{ + /******* 引入库 *******/ + using TimeUtil for *; + using SafeMath for *; + event PROPOSAL(string proposalid,string Userid,string SuggestContent,string time);//提议事件.提议人ID,提议标题,内容。时间 + event APPROVAL(string proposalid,string Userid,string time);//提议事件,谁什么时间投了赞成票 + event DISAPPROVAL(string proposalid,string Userid,string time);//提议事件,谁什么时间投了赞成票 + /* + * 构造函数,初始化使用到的表结构 + * + * @param 无 + * + * @return 无 + */ + constructor() public{ + //初始化需要用到的表。建言献策表 + initTableStruct(t_proposal_struct, TABLE_PROPOSAL_NAME, TABLE_PROPOSAL_PRIMARYKEY, TABLE_PROPOSAL_FIELDS); + } + /* + * 1.用户提议 + * + * @param _proposalid 提议ID号 + * @param user_id 提议人ID + * @param proposal_content 提议内容 + * @param proposal_time 提议时间(自动获取) + * @param approval_cnt 支持票数(默认0) + * @param disapproval_cnt 反对票数(默认0) + * @param check_result 审核结果(默认审核中) + * @return 执行状态码 + * + * 测试举例 参数一:"17846" + * 参数二:"尊敬的领导,本人通过网络知道了了去年事务部和十大银行签了优抚协议,其它省份落实的很好,希望江西省也能对接下江西银行,江西农商银行等" + *注册成功返回SUCCESS,以及提议的句子。否则返回错误码,错误码对应的问题请参考TableDefTools写的 + */ + function suggestUser(string memory _proposalid,string memory _userid,string memory _content) public returns(int8){ + //获取时间 + string memory nowDate = TimeUtil.getNowDate(); + + // string memory storeFields = StringUtil.strConcat5(_userid,',',suggestfields,',',nowDate); + string memory lastThreeParams = "0,0,审核中"; + string memory storeFields = StringUtil.strConcat7(_userid,',',_content,',',nowDate,',',lastThreeParams); + emit PROPOSAL(StringUtil.strConcat2("提议的ID号为:",_proposalid),StringUtil.strConcat2("提议人的ID为:",_userid),StringUtil.strConcat2("提议内容为:",_content),StringUtil.strConcat2("提议时间为:",nowDate)); + return (insertOneRecord(t_proposal_struct,_proposalid,storeFields,false)); + } + /* + * 2.根据提议ID号查询提议人ID号和提议内容并以Json字符串方式输出 + * + * @param _proposalid 提议id + * + * @return 执行状态码 + * @return 该用户所有提议信息的Json字符串 + * + * 测试举例 参数一:"17846" + */ + function getSuggestRecordJson(string _proposalid) public view returns(int8, string){ + return selectOneRecordToJson(t_proposal_struct, _proposalid); + } + /* + * 3.据提议ID号查询提议人ID号和提议内容并以字符串数组方式输出 + * + * @param _proposalid 提议id + * + * @return 执行状态码 + * @return 该提议人ID和提议内容信息的字符串数组 + * + * 测试举例 参数一:"17846" + */ + function getSuggestRecordArray(string _proposalid) public view returns(int8, string[]){ + return selectOneRecordToArray(t_proposal_struct,_proposalid, ["proposal_id",_proposalid]); + } + /* + * 4.根据提议ID号查看提议内容 + * + * @param _proposalid 提议id + * + * @return 提议内容 + * + * 测试举例 参数一:"17846" + */ + function getSuggestContent(string _proposalid) public view returns(string){ + int8 retCode; + string[] memory retArray; + (retCode, retArray) = getSuggestRecordArray(_proposalid); + return retArray[1]; + } + /* + * 5.根据提议ID号查看提议人ID号, + * + * @param _proposalid 提议id + * + * @return 提议人Id + * + * 测试举例 参数一:"17846" + */ + function getSuggestId(string _proposalid) public view returns(string){ + int8 retCode; + string[] memory retArray; + (retCode, retArray) = getSuggestRecordArray(_proposalid); + return retArray[0]; + } + /* + * 6.根据提议ID号查看当前提议的支持票数 + * + * @param _proposalid 提议id + * + * @return 提议人Id + * + * 测试举例 参数一:"17846" + */ + function getApproval(string _proposalid) public view returns(uint){ + int8 retCode; + uint proposalApproval; + string[] memory retArray; + (retCode, retArray) = getSuggestRecordArray(_proposalid); + if(retCode == SUCCESS_RETURN){ + proposalApproval=TypeConvertUtil.stringToUint(retArray[3]); + return proposalApproval; + }else{ + return uint(-1); + } + + } + + /* + * 7根据提议ID号查看当前提议的反对票数 + * + * @param _proposalid 提议id + * + * @return 提议人Id + * + * 测试举例 参数一:"17846" + */ + function getDisApproval(string _proposalid) public view returns(uint){ + int8 retCode; + uint proposalApproval; + string[] memory retArray; + (retCode, retArray) = getSuggestRecordArray(_proposalid); + if(retCode == SUCCESS_RETURN){ + proposalApproval=TypeConvertUtil.stringToUint(retArray[4]); + return proposalApproval; + }else{ + return uint(-1); + } + } + /* + * 8根据提议ID号查看当前提议的审核结果 + * + * @param _proposalid 提议id + * + * @return 提议人Id + * + * 测试举例 参数一:"17846" + */ + function getResult(string _proposalid) public view returns(string){ + int8 retCode; + string[] memory retArray; + (retCode, retArray) = getSuggestRecordArray(_proposalid); + return retArray[5]; + } + /* + * 9.向proposal表进行投赞成票操作 + * + * @param _proposalid 提议id,唯一主键 + * @param _userid 投票人的ID + * @return 执行状态码 + *测试举例 参数一:"191867345212322",123,1 + */ + function Approval(string memory _proposalid,string memory _userid) public returns(int8) { + // 该提案当前支持票票数 + uint proposalHasApproval; + // 该提案更新后的支持票票数 + uint proposalNowApproval; + // 查询信息返回状态 + int8 queryRetCode; + // 更新信息返回状态 + int8 updateRetCode; + // 数据表返回信息 + string[] memory retArray; + // 获得当前的时间 + string memory updateTime= TimeUtil.getNowDate(); + // 查看该用户记录信息 + (queryRetCode, retArray) = selectOneRecordToArray(t_proposal_struct,_proposalid, ["proposal_id",_proposalid]); + proposalHasApproval=getApproval(_proposalid); + proposalNowApproval=SafeMath.add(proposalHasApproval,1); + emit APPROVAL(StringUtil.strConcat2("提议的ID号为:",_proposalid),StringUtil.strConcat2("已投支持票,该投票人的ID为:",_userid),StringUtil.strConcat2("投票时间为:",updateTime)); + string memory changedFieldsStr = getChangeFieldsString(retArray,3, TypeConvertUtil.uintToString(proposalNowApproval));//转换回字符串类型 + if(proposalNowApproval>9){ + string memory changedFieldsStr4 = getChangeFieldsString(retArray,5,"支持"); + return(updateOneRecord(t_proposal_struct,_proposalid,changedFieldsStr4)); + } + return(updateOneRecord(t_proposal_struct,_proposalid,changedFieldsStr)); + } + /* + * 10.向proposal表进行投赞成票操作 + * + * @param _proposalid 提议id,唯一主键 + * @param _userid 投票人的ID + + * @return 执行状态码 + *测试举例 参数一:"191867345212322",1 + */ + function DisApproval(string memory _proposalid,string memory _userid) public returns(int8) { + + + // 该提案当前反对票票数 + uint proposalHasDisApproval; + + // 该提案更新后的反对票票数 + uint proposalNowDisApproval; + + // 查询信息返回状态 + int8 queryRetCode; + // 更新信息返回状态 + int8 updateRetCode; + // 数据表返回信息 + string[] memory retArray; + // 获得当前的时间 + string memory updateTime= TimeUtil.getNowDate(); + // 查看该用户记录信息 + (queryRetCode, retArray) = selectOneRecordToArray(t_proposal_struct,_proposalid, ["proposal_id",_proposalid]); + //该提案存在 + //将提案票数转换为整数进行操作selectOneRecordToArray + proposalHasDisApproval=getDisApproval(_proposalid); + + // 更新投票结果 + proposalNowDisApproval=SafeMath.add(proposalHasDisApproval,1);//调用Safemath的减法操作,避免溢出问题 + //对信用分操作完毕后。更新用户表 + string memory changedFieldsStr = getChangeFieldsString(retArray,4, TypeConvertUtil.uintToString(proposalNowDisApproval)); + //过半通过/不通过 + if(proposalNowDisApproval>9){ + string memory changedFieldsStr3 = getChangeFieldsString(retArray,5,"反对"); + return(updateOneRecord(t_proposal_struct,_proposalid,changedFieldsStr3)); + } + emit DISAPPROVAL(StringUtil.strConcat2("提议的ID号为:",_proposalid),StringUtil.strConcat2("已投反对票,该投票人的ID为:",_userid),StringUtil.strConcat2("投票时间为:",updateTime)); + return(updateOneRecord(t_proposal_struct,_proposalid,changedFieldsStr)); + } +} + + diff --git a/2021-BeiJing-BSN3/PRG/Contracts/report.sol b/2021-BeiJing-BSN3/PRG/Contracts/report.sol new file mode 100644 index 000000000..61c7535f9 --- /dev/null +++ b/2021-BeiJing-BSN3/PRG/Contracts/report.sol @@ -0,0 +1,101 @@ +pragma solidity ^0.4.25; +pragma experimental ABIEncoderV2; +import "./lib/SafeMath.sol"; +import "./utils/TimeUtil.sol"; +import "./TableDefTools.sol"; + +/* +* +* report实现用户反馈。用户可以对其他人进行举报或者表扬好人好事 +* 操作的表为t_report.主键为report_id +* user_id,to_user_id,event_content,event_type +*/ +contract report is TableDefTools{ + /******* 引入库 *******/ + using TimeUtil for *; + using SafeMath for *; + //反馈人在XX时间反馈XXX做了XXX事情,该事情类别为X + event FEEDBACK(string reportId,string Userid,string ToUserid,string event_content,string time); + /* + * 构造函数,初始化使用到的表结构 + * + * @param 无 + * + * @return 无 + */ + constructor() public{ + //初始化需要用到的表。反馈表 + initTableStruct(t_report_struct, TABLE_REPORT_NAME, TABLE_REPORT_PRIMARYKEY, TABLE_REPORT_FIELDS); + } + /* + * 1.用户进行反馈 + * + * @param _reportid 反馈ID号唯一主键 + * @param _userid 反馈ID + * @param _toUserid 被反馈对象id + * @param _content 反馈内容 + * @param _type 反馈类别 0为好人好事 1为举报做了坏事 + * @return 执行状态码 + * + * 测试举例 参数一:"17846" + * 参数二:"1","2","张三欠钱一直迟迟不还","1" + *注册成功返回SUCCESS,以及留言的句子。否则返回错误码,错误码对应的问题请参考TableDefTools写的 + */ + function feedback(string memory _reportId,string memory _userid,string memory _toUserid,string memory _content,string memory _type) public returns(int8){ + //获取时间 + string memory nowDate = TimeUtil.getNowDate(); + + string memory storeFields = StringUtil.strConcat7(_userid,',',_toUserid,',',_content,',',_type); + + + emit FEEDBACK(StringUtil.strConcat2("反馈的ID号为:",_reportId),StringUtil.strConcat2("反馈人的ID为:",_userid),StringUtil.strConcat2("反馈对象的ID为:",_toUserid),StringUtil.strConcat2("反馈内容为:",_content),StringUtil.strConcat2("反馈时间为:",nowDate)); + + + return (insertOneRecord(t_report_struct,_reportId,storeFields,false)); + } + /* + * 2.根据反馈ID号查询反馈内容并以Json字符串方式输出 + * + * @param _reportid 反馈id + * + * @return 执行状态码 + * @return 该用户所有留言信息的Json字符串 + * + * 测试举例 参数一:"17846" + */ + function getReportJson(string _reportid) public view returns(int8, string){ + return selectOneRecordToJson(t_report_struct, _reportid); + } + /* + * 3.据留言ID号查询留言人ID号和留言内容并以字符串数组方式输出 + * + * @param _reportid 留言id + * + * @return 执行状态码 + + * + * 测试举例 参数一:"17846" + */ + function getReportArray(string _reportid) public view returns(int8, string[]){ + return selectOneRecordToArray(t_report_struct,_reportid, ["report_id",_reportid]); + } + /* + * 4.根据留言ID号查看留言内容,最后通过这个内容去调用文本审核api + * + * @param _reportid 留言id + * + * @return 留言内容 + * + * 测试举例 参数一:"17846" + */ + function getReportContent(string _reportid) public view returns(string){ + int8 retCode; + string[] memory retArray; + (retCode, retArray) = getReportArray(_reportid); + return retArray[2]; + } + + +} + + diff --git a/2021-BeiJing-BSN3/PRG/Contracts/utils/StringUtil.sol b/2021-BeiJing-BSN3/PRG/Contracts/utils/StringUtil.sol new file mode 100644 index 000000000..b723c9214 --- /dev/null +++ b/2021-BeiJing-BSN3/PRG/Contracts/utils/StringUtil.sol @@ -0,0 +1,292 @@ +pragma solidity ^0.4.25; +pragma experimental ABIEncoderV2; + +import "../lib/Table.sol"; + +/* +* 处理字符串的工具合约 +* +*/ +library StringUtil { + + /* + * 比较两个字符串是否相符 + * + * @param string1 各字段名 + * @param string2 记录的实例 + * + * @return 各字段值 + */ + + function compareTwoString(string string1, string string2) pure internal returns (bool) { + if (bytes(string1).length != bytes(string2).length) { + return false; + } else { + return keccak256(string1) == keccak256(string2); + } + } + /* + * 得到表中指定一条记录的值 + * + * @param fields 各字段名 + * @param fields 记录的实例 + * + * @return 各字段值 + */ + function getEntry(string[] memory fields, Entry entry) internal view returns (string[] memory) { + string[] memory values = new string[](fields.length); + for (uint i = 0; i < fields.length; i++) { + values[i] = entry.getString(fields[i]); + } + return values; + } + + + /* + * 得到表中指定一条记录的值,以Json字符串格式输出 + * + * @param fields 各字段名 + * @param primaryKey 主键值 + * @param entries 多条记录的实例 + * + * @return 执行状态码 + * @return 记录值 + */ + function getJsonString(string[] memory fields,string primaryKey, Entries entries) internal view returns (int8, string memory) { + string memory detail; + if (0 == entries.size()) { + return (int8(-2), detail); + } + else { + detail = "["; + for (uint i = 0; i < uint(entries.size()); i++) { + string[] memory values = getEntry(fields, entries.get(int(i))); + for (uint j = 0; j < values.length; j++) { + if (j == 0) { + detail = strConcat4(detail, "{\"primaryKey\":\"", primaryKey, "\",\"fields\":{");//这里订正了!!fields左边缺个双引号 + } + detail = strConcat6(detail, "\"", fields[j], "\":\"", values[j], "\""); + + if (j == values.length - 1) { + detail = strConcat2(detail, "}}"); + } else { + detail = strConcat2(detail, ","); + } + } + if (i != uint(entries.size()) - 1) { + detail = strConcat2(detail, ","); + } + } + detail = strConcat2(detail, "]"); + return (int8(1), detail); + } + } + /* + * 字符串拼接工具(后面strConcatX都为字符串拼接工具方法,X为字符串数量) + * + * @param str1 待拼接字符串1 + * @param str2 待拼接字符串2 + * @param str3 待拼接字符串3 + * @param str4 待拼接字符串4 + * @param str5 待拼接字符串5 + * @param str6 待拼接字符串6 + * + * @return 拼接完成后的字符串 + */ + function strConcat8( + string memory str1, + string memory str2, + string memory str3, + string memory str4, + string memory str5, + string memory str6, + string memory str7, + string memory str8 + ) internal pure returns (string memory) { + string[] memory strings = new string[](7); + strings[0] = str1; + strings[1] = str2; + strings[2] = str3; + strings[3] = str4; + strings[4] = str5; + strings[5] = str6; + strings[6] = str7; + strings[7] = str8; + return strConcat(strings); + } + /* + * 字符串拼接工具(后面strConcatX都为字符串拼接工具方法,X为字符串数量) + * + * @param str1 待拼接字符串1 + * @param str2 待拼接字符串2 + * @param str3 待拼接字符串3 + * @param str4 待拼接字符串4 + * @param str5 待拼接字符串5 + * @param str6 待拼接字符串6 + * + * @return 拼接完成后的字符串 + */ + function strConcat7( + string memory str1, + string memory str2, + string memory str3, + string memory str4, + string memory str5, + string memory str6, + string memory str7 + ) internal pure returns (string memory) { + string[] memory strings = new string[](7); + strings[0] = str1; + strings[1] = str2; + strings[2] = str3; + strings[3] = str4; + strings[4] = str5; + strings[5] = str6; + strings[6] = str7; + return strConcat(strings); + } + + /* + * 字符串拼接工具(后面strConcatX都为字符串拼接工具方法,X为字符串数量) + * + * @param str1 待拼接字符串1 + * @param str2 待拼接字符串2 + * @param str3 待拼接字符串3 + * @param str4 待拼接字符串4 + * @param str5 待拼接字符串5 + * @param str6 待拼接字符串6 + * + * @return 拼接完成后的字符串 + */ + function strConcat6( + string memory str1, + string memory str2, + string memory str3, + string memory str4, + string memory str5, + string memory str6 + ) internal pure returns (string memory) { + string[] memory strings = new string[](6); + strings[0] = str1; + strings[1] = str2; + strings[2] = str3; + strings[3] = str4; + strings[4] = str5; + strings[5] = str6; + return strConcat(strings); + } + + function strConcat5( + string memory str1, + string memory str2, + string memory str3, + string memory str4, + string memory str5 + ) internal pure returns (string memory) { + string[] memory strings = new string[](5); + strings[0] = str1; + strings[1] = str2; + strings[2] = str3; + strings[3] = str4; + strings[4] = str5; + return strConcat(strings); + } + + function strConcat4( + string memory str1, + string memory str2, + string memory str3, + string memory str4 + ) internal pure returns (string memory) { + string[] memory strings = new string[](4); + strings[0] = str1; + strings[1] = str2; + strings[2] = str3; + strings[3] = str4; + return strConcat(strings); + } + + function strConcat3( + string memory str1, + string memory str2, + string memory str3 + ) internal pure returns (string memory) { + string[] memory strings = new string[](3); + strings[0] = str1; + strings[1] = str2; + strings[2] = str3; + return strConcat(strings); + } + + function strConcat2(string memory str1, string memory str2) internal pure returns (string memory) { + string[] memory strings = new string[](2); + strings[0] = str1; + strings[1] = str2; + return strConcat(strings); + } + + function strConcat(string[] memory strings) internal pure returns (string memory) { + // 计算字节长度 + uint bLength = 0; + for (uint i = 0; i < strings.length; i++) { + bLength += bytes(strings[i]).length; + } + + // 实例化字符串 + string memory result = new string(bLength); + bytes memory bResult = bytes(result); + + // 填充字符串 + uint currLength = 0; + for ( i = 0; i < strings.length; i++) { + // 将当前字符串转换为字节数组 + bytes memory bs = bytes(strings[i]); + for (uint j = 0; j < bs.length; j++) { + bResult[currLength] = bs[j]; + currLength++; + } + } + return string(bResult); + } + + + /* + * 字符串数组拼接为字符串的工具,各字符串之间以逗号进行连接。 + * + * @param strings 字符串数组 + * + * @return 拼接后的字符串 + */ + function strConcatWithComma(string[] memory strings) internal pure returns (string memory) { + + uint bLength = 0; + string memory commaString = ","; + bytes memory oneCommaBytes = bytes(commaString); + + for (uint i = 0; i < strings.length; i++) { + if(i > 0){ + bLength += oneCommaBytes.length; + } + bLength += bytes(strings[i]).length; + } + string memory result = new string(bLength); + bytes memory bResult = bytes(result); + uint currLength = 0; + for ( i = 0; i < strings.length; i++) { + bytes memory bs = bytes(strings[i]); + if(i > 0){ + for(uint k=0;k < oneCommaBytes.length; k++){ + bResult[currLength] = oneCommaBytes[k]; + currLength++; + } + } + for (uint j = 0; j < bs.length; j++) { + bResult[currLength] = bs[j]; + currLength++; + } + } + + return string(bResult); + } + +} \ No newline at end of file diff --git a/2021-BeiJing-BSN3/PRG/Contracts/utils/TimeUtil.sol b/2021-BeiJing-BSN3/PRG/Contracts/utils/TimeUtil.sol new file mode 100644 index 000000000..18dcd97bb --- /dev/null +++ b/2021-BeiJing-BSN3/PRG/Contracts/utils/TimeUtil.sol @@ -0,0 +1,94 @@ +pragma solidity ^0.4.25; + +import "../lib/DateTimeLibrary.sol"; +import "./StringUtil.sol"; +import "./TypeConvertUtil.sol"; + +/* +* 获取当前时间的工具合约 +* +*/ +library TimeUtil { + + using DateTimeLibrary for uint; + using StringUtil for *; + using TypeConvertUtil for *; + + /* + * 得到当下的日期及时间,分别以字符串方式输出 + * + * @param无,直接调用 + * + * @return dateString 当前日期字符串 + * @return timeString 当前时间字符串 + * + */ + function getNowDateTime() internal view returns(string dateString, string timeString){ + (uint year,uint month,uint day,uint hour,uint minute,uint second) = DateTimeLibrary.timestampToDateTime(now/1000); + string memory nowDate = StringUtil.strConcat5( + TypeConvertUtil.uintToString(year), + "-" , + TypeConvertUtil.uintToString(month), + "-" , + TypeConvertUtil.uintToString(day)); + string memory nowTime = StringUtil.strConcat5( + TypeConvertUtil.uintToString(hour), + ":" , + TypeConvertUtil.uintToString(minute), + ":" , + TypeConvertUtil.uintToString(second)); + return (nowDate, nowTime); + } + /* + * 得到当下的日期及时间,以字符串方式直接输出 + * + * @param无,直接调用 + * + * @return dateString 当前日期字符串 + * @return timeString 当前时间字符串 + * + */ + function getNowTime() internal view returns(string nowtimeString){ + (uint year,uint month,uint day,uint hour,uint minute,uint second) = DateTimeLibrary.timestampToDateTime(now/1000); + string memory nowDate = StringUtil.strConcat5( + TypeConvertUtil.uintToString(year), + "-" , + TypeConvertUtil.uintToString(month), + "-" , + TypeConvertUtil.uintToString(day)); + string memory nowTime = StringUtil.strConcat5( + TypeConvertUtil.uintToString(hour), + ":" , + TypeConvertUtil.uintToString(minute), + ":" , + TypeConvertUtil.uintToString(second)); + string memory NowDefiniteTime=StringUtil.strConcat3(nowDate,'日',nowTime); + return (NowDefiniteTime); + } + + /* + * 得到当下的日期,以字符串方式输出 + * + * @param无,直接调用 + * + * @return dateString 当前日期字符串 + * @return timeString 当前时间字符串 + * + */ + function getNowDate() internal view returns(string dateString){ + (uint year,uint month,uint day) = DateTimeLibrary.timestampToDate(now/1000); + string memory nowDate = StringUtil.strConcat5( + TypeConvertUtil.uintToString(year), + "-" , + TypeConvertUtil.uintToString(month), + "-" , + TypeConvertUtil.uintToString(day)); + return nowDate; + } + +} + + + + + diff --git a/2021-BeiJing-BSN3/PRG/Contracts/utils/TypeConvertUtil.sol b/2021-BeiJing-BSN3/PRG/Contracts/utils/TypeConvertUtil.sol new file mode 100644 index 000000000..25f3b95d0 --- /dev/null +++ b/2021-BeiJing-BSN3/PRG/Contracts/utils/TypeConvertUtil.sol @@ -0,0 +1,124 @@ +pragma solidity ^0.4.25; + +/* +* solidity类型转换工具合约 +* +*/ +library TypeConvertUtil{ + + /* + * 将string转化为bytes32的工具方法 + * + * @param source 字符串 + * + * @return bytes32值 + */ + function stringToBytes32(string memory source) internal pure returns (bytes32 result) { + + bytes memory tempEmptyStringTest = bytes(source); + if (tempEmptyStringTest.length == 0) { + return 0x0; + } + assembly { + result := mload(add(source, 32)) + } + } + + + /* + * 将bytes32转化为string的工具方法 + * + * @param source bytes32值 + * + * @return 字符串 + */ + function bytes32ToString(bytes32 source) internal pure returns (string) { + bytes memory bytesString = new bytes(32); + uint charCount = 0; + for (uint j = 0; j < 32; j++) { + byte char = byte(bytes32(uint(source) * 2 ** (8 * j))); + if (char != 0) { + bytesString[charCount] = char; + charCount++; + } + } + bytes memory bytesStringTrimmed = new bytes(charCount); + for (j = 0; j < charCount; j++) { + bytesStringTrimmed[j] = bytesString[j]; + } + return string(bytesStringTrimmed); + } + + + /* + * 将string转化为uint的工具方法 + * + * @param source 字符串 + * + * @return uint值 + */ + function stringToUint(string source) internal pure returns (uint result) { + bytes memory b = bytes(source); + uint i; + result = 0; + for (i = 0; i < b.length; i++) { + uint c = uint(b[i]); + if (c >= 48 && c <= 57) { + result = result * 10 + (c - 48); + } + } + } + + + /* + * 将uint转化为string的工具方法 + * + * @param source uint值 + * + * @return 字符串 + */ + function uintToString(uint source) internal pure returns (string){ + if (source == 0) return "0"; + uint j = source; + uint length; + while (j != 0){ + length++; + j /= 10; + } + bytes memory bstr = new bytes(length); + uint k = length - 1; + while (source != 0){ + bstr[k--] = byte(48 + source % 10); + source /= 10; + } + return string(bstr); + } + + + /* + * 将bytes转化为address的工具方法 + * + * @param source bytes值 + * + * @return address值 + */ + function bytesToAddress (bytes source) internal pure returns (address) { + uint result = 0; + for (uint i = 0; i < source.length; i++) { + uint c = uint(source[i]); + if (c >= 48 && c <= 57) { + result = result * 16 + (c - 48); + } + if(c >= 65 && c<= 90) { + result = result * 16 + (c - 55); + } + if(c >= 97 && c<= 122) { + result = result * 16 + (c - 87); + } + } + return address(result); + } + + + +} \ No newline at end of file diff --git a/2021-BeiJing-BSN3/PRG/README.md b/2021-BeiJing-BSN3/PRG/README.md new file mode 100644 index 000000000..e9d9c7e38 --- /dev/null +++ b/2021-BeiJing-BSN3/PRG/README.md @@ -0,0 +1,30 @@ +# 开拓者-数字金融与乡村治理 + +## 项目简介 + +“开拓者”项目是基于区块链可信大数据AI的下一代的“数字金融+农村社区”治理优化系统,通过将村庄居民的基础情况与行为分类,转化为积分等可以量化的信息,全部上链管理,对个体进行评价。主要包括三个方面: + +- ①积分量化行为进行奖惩,区块链通证经济激励。并通过积分兑换和公式等方式对居民进行激励,鼓励向好。 +- ②AI 社交网络,关键节点让治理更高效。居民的行为不仅用于转换积分,还可以被追踪记录,并呈现为社交网络图,方便居民对个人关系及村庄对居民进行管理。 +- ③积分助力金融,可信大数据智能风控。积分还可以进一步计算以衡量居民个人信用,促进决策。 + +## 项目背景 + +- 2019年6月,中共中央办公厅、国务院办公厅印发《关于加强和改进乡村治理的指导意见》,并发出通知,要求各地区各部门结合实际认真贯彻落实。实现乡村有效治理是乡村振兴的重要内容。农村基层组织,村民自治。 +- 2014 年 3 月银监会发布了《农村中小金融机构行政许可事项实施办法》,放宽了村镇银行准入门槛,为农户提供更便利的金融服务。2019 年中央一号文件提出,打通金融服务“三农”各个环节,建立县域银行业金融机构服务“三农”的激励约束机制。 +农村小额信贷+农村保险 + +## 方案 +- 居民首次注册后,在“我的”界面中,通过“信息填报”选项进行填报;然后通过发布/领取任务/建言献策进行事务求助或积分获取,与此同时还可以建言献策;居民的行为会连接其他人,据此会产生关系网络图,居民可以通过“我的-我的关系图”查看自己与其他人的关系密切程度; +- 此外,居民的基本信息和个人行为积分会被内部计算后得出信用积分,居民据此参加融资等活动。 + +![avatar](./img/img1.png) + +## 团队成员 + +|作品名|参赛队员姓名|学校/公司名| +|-----|---------|----------| +|数字金融与乡村治理|蒲沧龙|浙江工业大学之江学院| +|数字金融与乡村治理|王宁波|无锡太湖学院| +|数字金融与乡村治理|江会文|江西师范大学| +|数字金融与乡村治理|罗智文|江西师范大学| diff --git a/2021-BeiJing-BSN3/PRG/img/README.MD b/2021-BeiJing-BSN3/PRG/img/README.MD new file mode 100644 index 000000000..60a9e38d3 --- /dev/null +++ b/2021-BeiJing-BSN3/PRG/img/README.MD @@ -0,0 +1 @@ +img files \ No newline at end of file diff --git a/2021-BeiJing-BSN3/PRG/img/img1.png b/2021-BeiJing-BSN3/PRG/img/img1.png new file mode 100644 index 000000000..8cb3f00d5 Binary files /dev/null and b/2021-BeiJing-BSN3/PRG/img/img1.png differ