//Write your own contracts here. Currently compiles using solc v0.4.15+commit.bbb8e64f. pragma solidity ^0.4.20; /************************************************************************************************************* 本合约涉及到仓单流转的的基本功能,权限控制等其他需求请用户根据自己需要酌情增加 |-------------| |-------------| |-------------| |-------------| | 4.移库中 | | 5.移库待审核 | --->| 9融资待审核 |--->| 10仓单融资 | |_____________|<---- |_____________|<------- | |_____________| |_____________| | | | | Y | |-------------| |-------------| | 2仓单待认证 |----------> | 7仓单正常 | ______|_____________| <------- |_____________| <-------- | |____________ | Y Y | |-------------| |-------------| |-------------| | | 3出库待审核 |--------> | 6仓单注销 | | 8仓单冻结 |<--- |_____________| |_____________| |_____________| 1. 管理员调用setEnterpriseScore,设置企业评分 2. 调用storageRegister,绑定货位与iot设备 3. iot 设备调用 uploadImageHash 上传监控图片哈希 4. 调用instoreWhApply入库申请 5. 调用createWhreceipt生成仓单 6. 认证仓单qmeVerifyWhReceipt 7. 移库出库申请moveoutstoreWhApply 8. 移库入库申请moveinstoreWhApply 9. 移库审核movestoreAudit 10. 认证仓单auditWhReceipt *************************************************************************************************************/ contract WareHouseManager { using strings for *; using utils for *; event LOG_STRING(string); struct InstoreWhApply{ string instore_apply_code; string apply_name; string appoint_date; string apply_desc; string user_agent_account; string[] receipt_ids; } /* receipt_status: 1.待审核 2.仓单待认证 3.出库待审核 4.移库中 5.移库待审核 6.仓单注销 7.仓单正常 8.仓单冻结 9.仓单融资待审核 10.仓单融资 */ struct WhReceipt{ string receipt_code;//仓单编号 string instore_apply_code;//入库申请号 uint receipt_status; string receipt_fillin_date; //入库时间 bool is_wms_verify; //是否认证仓单 string instore_name;//存货人姓名 string custody_name;//保管人姓名 string delivery_date;//仓库发货时间 string shipper_name;//发货人姓名 string consignee_name;//提货人姓名 string consignee_date;//提货时间 string receipt_owner;//仓单权属人 string store_goods_code;//仓储物编号 string store_goods_name;//仓储物名称 string store_goods_spec;//仓储物规格 string store_goods_uint;//仓储物单位 string store_goods_num;//仓储物数量 string store_goods_gps;//仓储物gps信息/货号信息 string image_info; string bank_no;//融资银行/金融机构号 string iot_id; // IOT设备编号 string wh_account; //仓储企业账号 } struct OutstoreWhApply{ string appoint_date; string goods_info; string[] receipt_code_list; string apply_desc; string user_agent_account; } struct MovestoreWhApply{ string movestore_apply_code; string[] movestore_receipt_code_list; } struct FinanceWhApply{ string finance_wh_code; string[] receipt_code_list; string bank_no; string finance_info; string finance_audit_desc; } mapping (string => uint) enterprise_scores; //仓储企业<-->评分 mapping (string => string) storeid_iotid; //货位编号<-->iot设备号 mapping (string => string) iotid_storeid; //iot设备号<-->货位编号 mapping (string => WhReceipt) wh_receipts; //所有仓单 仓单编号<-->仓单 mapping (string => InstoreWhApply) wh_applies; //所有入库申请 申请编号<-->入库申请 mapping (string => string) image_hashes; //所有图片的哈希 mapping (string => OutstoreWhApply) out_wh_applies;//所有出库申请 申请编号 <-->出库申请 mapping (string => FinanceWhApply) finance_applies;//所有融资申请 申请编号 <-->融资申请 mapping (string => MovestoreWhApply) move_applies;//所有移库申请 申请编号 <-->移库申请 mapping (string => string) txhash_receipt_code; //电子仓单txhash查找receipt_code //仓储企业评分 // setEnterpriseScores("1000",100) function setEnterpriseScore(string enterprise,uint score) public returns (bool){ enterprise_scores[enterprise] = score; string memory json = "set enterprise score "; json = json.toSlice().concat(enterprise.toSlice()); json = json.toSlice().concat(score.uintToString().toSlice()); emit LOG_STRING; return true; } //IOT注册仓库ID // storageRegister("storage_id","iot_id") function storageRegister(string storage_id,string iot_id) public returns (bool){ if(!storeid_iotid[storage_id].compare_string("")){ return false; } if(!iotid_storeid[iot_id].compare_string("")){ return false; } storeid_iotid[storage_id] = iot_id; iotid_storeid[iot_id]=storage_id; string memory json = "storage register "; json = json.toSlice().concat(storage_id.toSlice()); json = json.toSlice().concat(":".toSlice()); json = json.toSlice().concat(iot_id.toSlice()); emit LOG_STRING; return true; } //根据iot设备号查找货位号 function getStorageIdByIotId(string iotid) public returns (string){ return iotid_storeid[iotid]; } //入库申请 function instoreWhApply(string instore_apply_code,string apply_name,string appoint_date,string apply_desc,string user_agent_account) public returns (bool){ InstoreWhApply memory ina; ina.instore_apply_code = instore_apply_code; ina.apply_name = apply_name; ina.appoint_date = appoint_date; ina.apply_desc = apply_desc; ina.user_agent_account = user_agent_account; ina.receipt_ids = new string[](0); wh_applies[instore_apply_code] = ina; string memory log = instore_apply_code; log = log.toSlice().concat(" apply instore success".toSlice()); emit LOG_STRING; return true; } //生成仓单 /** { "instore_apply_code":"instore_apply_code", "receipt_code":"receipt_code", "receipt_fillin_date":"receipt_fillin_date", "instore_name":"instore_name", "custody_name":"custody_name", "receipt_owner":"receipt_owner", "store_goods_code":"store_goods_code", "store_goods_name":"store_goods_name", "store_goods_uint":"store_goods_uint", "store_goods_num":"store_goods_num", "store_goods_gps":"storage_id", "image_info":"xxxx", "wh_account":"enterpriseId", "iot_id":"iot_id" } **/ function createWhList(string json ) public returns (bool){ // 第一步调用property_parse uint handler = property_parse(json, 0); string[] memory parameter = new string[](14); int err; (err,parameter[0]) = property_get_string(handler,"instore_apply_code"); if(err!=0)return false; (err,parameter[1]) = property_get_string(handler,"receipt_code"); if(err!=0)return false; (err,parameter[2]) = property_get_string(handler,"receipt_fillin_date"); if(err!=0)return false; (err,parameter[3]) = property_get_string(handler,"instore_name"); if(err!=0)return false; (err,parameter[4]) = property_get_string(handler,"custody_name"); if(err!=0)return false; (err,parameter[5]) = property_get_string(handler,"receipt_owner"); if(err!=0)return false; (err,parameter[6]) = property_get_string(handler,"store_goods_code"); if(err!=0)return false; (err,parameter[7]) = property_get_string(handler,"store_goods_name"); if(err!=0)return false; (err,parameter[8]) = property_get_string(handler,"store_goods_uint"); if(err!=0)return false; (err,parameter[9]) = property_get_string(handler,"store_goods_num"); if(err!=0)return false; (err,parameter[10]) = property_get_string(handler,"store_goods_gps"); if(err!=0)return false; (err,parameter[11]) = property_get_string(handler,"image_info"); if(err!=0)return false; (err,parameter[12]) = property_get_string(handler,"wh_account"); if(err!=0)return false; (err,parameter[13]) = property_get_string(handler,"iot_id"); if(err!=0)return false; if(image_hashes[parameter[11]].compare_string("")){ return (false); } if(!iotid_storeid[parameter[13]].compare_string(parameter[10])){ return false; } if(!storeid_iotid[parameter[10]].compare_string(parameter[13])){ return false; } if(enterprise_scores[parameter[12]]<9){ return false; } WhReceipt memory tmpwr; InstoreWhApply tmpina; tmpwr.receipt_code = parameter[1]; tmpwr.instore_apply_code = parameter[0]; tmpwr.receipt_fillin_date = parameter[2]; tmpwr.instore_name = parameter[3]; tmpwr.custody_name = parameter[4]; tmpwr.receipt_owner = parameter[5]; tmpwr.store_goods_code = parameter[6]; tmpwr.store_goods_name = parameter[7]; tmpwr.store_goods_uint = parameter[8]; tmpwr.store_goods_num = parameter[9]; tmpwr.store_goods_gps = parameter[10]; tmpwr.receipt_status = 7; tmpwr.image_info = parameter[11]; tmpwr.is_wms_verify = true; tmpwr.tx_hash = tx.txhash; tmpwr.iot_id = parameter[13]; tmpwr.wh_account = parameter[12]; tmpina = wh_applies[tmpwr.instore_apply_code]; if(tmpina.instore_apply_code.compare_string("")){ return (false); } wh_receipts[tmpwr.receipt_code] = tmpwr; txhash_receipt_code[bytes32(tmpwr.tx_hash).bytesToHexString()] = tmpwr.receipt_code; tmpina.receipt_ids[tmpina.receipt_ids.length++] = tmpwr.receipt_code; emit LOG_STRING; return (true); } function getReceiptInfoByTxHash(string hash) public returns (string){ string memory receipt_code = txhash_receipt_code[hash]; if(receipt_code.compare_string("")){ return ""; } WhReceipt wr = wh_receipts[receipt_code]; string memory wr_json = getWhReceiptInfo(wr); return wr_json; } //认证仓单 function qmeVerifyWhReceipt(string instore_apply_code)public returns (bool){ InstoreWhApply ina = wh_applies[instore_apply_code]; for(uint i=0;i<ina.receipt_ids.length;i++){ WhReceipt wr = wh_receipts[ina.receipt_ids[i]]; if(wr.receipt_status==2){ wr.receipt_status = 7; wr.is_wms_verify = true; }else if(wr.is_wms_verify){ if(wr.receipt_status==5){ wr.receipt_status = 7; } if(wr.receipt_status==8){ wr.receipt_status = 7; } } } string memory log = instore_apply_code; log = log.toSlice().concat(" qme verify receipt success".toSlice()); emit LOG_STRING; return true; } //生效仓单状态 function auditWhReceipt(string instore_apply_code,bool is_pass,string receipt_owner)public returns (bool){ if(!is_pass){ return true; } InstoreWhApply ina = wh_applies[instore_apply_code]; bool isAllReceiptValid = false; for(uint i=0;i<ina.receipt_ids.length;i++){ WhReceipt wr = wh_receipts[ina.receipt_ids[i]]; if(wr.receipt_status==1){ wr.receipt_status = 2; wr.receipt_owner = receipt_owner; isAllReceiptValid = true; continue; }else if (wr.receipt_status==5 && !wr.is_wms_verify){ wr.receipt_status = 2; wr.receipt_owner = receipt_owner; isAllReceiptValid = true; continue; }else if (wr.receipt_status==2){ wr.receipt_owner = receipt_owner; isAllReceiptValid = true; continue; }else{ } } string memory log = instore_apply_code; log = log.toSlice().concat(" audit receipt success".toSlice()); emit LOG_STRING; return isAllReceiptValid; } /* "receipt_code":{"receipt_code":"receipt_code","instore_apply_code":"instore_apply_code",......} */ function getWhReceiptInfo(WhReceipt wr) private returns (string){ if(wr.receipt_code.compare_string("")){ return ""; } //if(!wr.bank_no.compare_string(bank_no)){ // return ""; //} string memory json = "\""; json = json.toSlice().concat(wr.receipt_code.toSlice()); json = json.toSlice().concat( "\":{".toSlice() ); strings.slice[] memory sl = new strings.slice[](35); sl[0] = "\"receipt_code\":\"".toSlice(); sl[1] = wr.receipt_code.toSlice(); sl[2] = "\",\"instore_apply_code\":\"".toSlice(); sl[3] = wr.instore_apply_code.toSlice(); sl[4] = "\",\"receipt_fillin_date\":\"".toSlice(); sl[5] = wr.receipt_fillin_date.toSlice(); sl[6] = "\",\"instore_name\":\"".toSlice(); sl[7] = wr.instore_name.toSlice(); sl[8] = "\",\"custody_name\":\"".toSlice(); sl[9] = wr.custody_name.toSlice(); sl[10] = "\",\"receipt_owner\":\"".toSlice(); sl[11] = wr.receipt_owner.toSlice(); sl[12] = "\",\"store_goods_code\":\"".toSlice(); sl[13] = wr.store_goods_code.toSlice(); sl[14] = "\",\"store_goods_name\":\"".toSlice(); sl[15] = wr.store_goods_name.toSlice(); sl[16] = "\",\"store_goods_uint\":\"".toSlice(); sl[17] = wr.store_goods_uint.toSlice(); sl[18] = "\",\"store_goods_num\":\"".toSlice(); sl[19] = wr.store_goods_num.toSlice(); sl[20] = "\",\"store_goods_gps\":\"".toSlice(); sl[21] = wr.store_goods_gps.toSlice(); sl[22] = "\",\"bank_no\":\"".toSlice(); sl[23] = wr.bank_no.toSlice(); sl[24] = "\",\"receipt_status\":".toSlice(); sl[25] = wr.receipt_status.uintToString().toSlice(); sl[26] = ",\"tx_hash\":\"".toSlice(); sl[27] = bytes32(wr.tx_hash).bytesToHexString().toSlice(); sl[28] = "\",\"image_info\":\"".toSlice(); sl[29] = wr.image_info.toSlice(); sl[30] = "\",\"iot_id\":\"".toSlice(); sl[31] = wr.iot_id.toSlice(); sl[32] = "\",\"wh_account\":\"".toSlice(); sl[33] = wr.wh_account.toSlice(); sl[34] = "\"".toSlice(); json = json.toSlice().concatA(sl); delete sl; json = json.toSlice().concat("}".toSlice()); return json; } //根据仓单编号查询仓单信息列表 function getWhReceiptInfoListByInstoreApplyCode(string instore_apply_code)public returns (string){ string memory json = "{"; InstoreWhApply ina = wh_applies[instore_apply_code]; for(uint i=0;i<ina.receipt_ids.length;i++){ WhReceipt wr = wh_receipts[ina.receipt_ids[i]]; string memory wr_json = getWhReceiptInfo(wr); if(wr_json.compare_string("")){ continue; } json = json.toSlice().concat(wr_json.toSlice()); delete wr_json; if(i<ina.receipt_ids.length-1){ json = json.toSlice().concat(",".toSlice()); } } json = json.toSlice().concat( "}".toSlice()); return json; } //根据仓单编号list查询仓单信息 function getWhReceiptInfoList(string receipt_info)public returns (string){ string[] memory receipt_code_list = getReceiptCodeList(receipt_info); string memory json = "{"; for(uint i=0;i<receipt_code_list.length;i++){ WhReceipt wr = wh_receipts[receipt_code_list[i]]; string memory wr_json = getWhReceiptInfo(wr); if(wr_json.compare_string("")){ continue; } json = json.toSlice().concat(wr_json.toSlice()); delete wr_json; if(i<receipt_code_list.length-1){ json = json.toSlice().concat(",".toSlice()); } } json = json.toSlice().concat( "}".toSlice()); return json; } /* { "receipt_code_list":["1111","2222","3333"] } */ function getReceiptCodeList(string json) private returns (string[]){ // 第一步调用property_parse uint handler = property_parse(json, 0); uint value = property_get_list_count(handler, "receipt_code_list"); string[] memory receipt_code_list = new string[](value); string memory receipt_code; int err; for(uint i=0;i<value;i++){ string memory key = "receipt_code_list["; key = key.toSlice().concat(i.uintToString().toSlice()); key = key.toSlice().concat("]".toSlice()); (err,receipt_code) = property_get_string(handler,key); delete key; if(err!=0){ continue; } receipt_code_list[i] = receipt_code; } delete err; delete receipt_code; //delete receipt_code_list; property_destroy(handler); return receipt_code_list; } //出库申请 function outstoreWhApply(string outstore_apply_code,string receipt_info,string apply_name,string appoint_date,string goods_info,string apply_desc,string user_agent_account)public returns (bool){ string[] memory receipt_code_list = getReceiptCodeList(receipt_info); //设置仓单为出库待审核 for(uint i=0;i<receipt_code_list.length;i++){ WhReceipt wr = wh_receipts[receipt_code_list[i]]; if(wr.receipt_status==2 && !wr.is_wms_verify){ wr.receipt_status = 3; }else if(wr.receipt_status==7 && wr.is_wms_verify){ wr.receipt_status = 3; } } OutstoreWhApply memory outa; outa.appoint_date = appoint_date; outa.goods_info = goods_info; outa.apply_desc = apply_desc; outa.user_agent_account = user_agent_account; outa.receipt_code_list = receipt_code_list; out_wh_applies[outstore_apply_code] = outa; string memory log = outstore_apply_code; log = log.toSlice().concat(" apply outstore success".toSlice()); emit LOG_STRING; return true; } //移出库申请 /* move_store_info: { "receipt_code_list":["1111","2222","3333"] } */ function moveoutstoreWhApply(string movestore_apply_code,string move_store_info)public returns (bool){ MovestoreWhApply memory mova; mova.movestore_apply_code = movestore_apply_code; mova.movestore_receipt_code_list = getReceiptCodeList(move_store_info); move_applies[movestore_apply_code] = mova; //mova.movestore_info = move_store_info; for(uint i=0;i<mova.movestore_receipt_code_list.length;i++){ WhReceipt wr = wh_receipts[mova.movestore_receipt_code_list[i]]; if(wr.receipt_status==2&&!wr.is_wms_verify){ wr.receipt_status = 4; }else if(wr.receipt_status==7&&wr.is_wms_verify){ wr.receipt_status = 4; } } string memory log = movestore_apply_code; log = log.toSlice().concat(" move out store apply success".toSlice()); emit LOG_STRING; return true; } //移入库申请 function moveinstoreWhApply(string movestore_apply_code,string move_store_info)public returns (bool){ MovestoreWhApply mova = move_applies[movestore_apply_code]; string[] memory receipt_code_list = mova.movestore_receipt_code_list; uint handler = property_parse(move_store_info, 0); uint value = property_get_list_count(handler, "receipt_list"); int err; for(uint i=0;i<value;i++){ string memory key = "receipt_list["; key = key.toSlice().concat(i.uintToString().toSlice()); key = key.toSlice().concat("].receiptCode".toSlice()); string memory string_value; (err,string_value) = property_get_string(handler,key); if(err!=0){ continue; } bool isInReceiptCodeList = false; for(uint j=0;j<receipt_code_list.length;j++){ if(receipt_code_list[j].compare_string(string_value)){ isInReceiptCodeList = true; } } if(isInReceiptCodeList){ WhReceipt wr = wh_receipts[string_value]; key = "receipt_list["; key = key.toSlice().concat(i.uintToString().toSlice()); key = key.toSlice().concat("].newStoreGoodsGps".toSlice()); (err,string_value) = property_get_string(handler,key); if(err!=0){ continue; } if(wr.receipt_status==4){ wr.store_goods_gps = string_value; wr.iot_id = storeid_iotid[string_value]; wr.receipt_status = 5; } } delete string_value; delete key; } string memory log = movestore_apply_code; log = log.toSlice().concat(" move in store apply success".toSlice()); emit LOG_STRING; return true; } //移库审核 function movestoreAudit(string movestore_apply_code,bool ispass)public returns (bool){ MovestoreWhApply mova = move_applies[movestore_apply_code]; string[] memory receipt_code_list = mova.movestore_receipt_code_list; for(uint i=0;i<receipt_code_list.length;i++){ WhReceipt wr = wh_receipts[receipt_code_list[i]]; if(wr.receipt_status!=5){ continue; } if(ispass){ if(wr.is_wms_verify){ wr.receipt_status = 7; }else{ wr.receipt_status = 2; } } } string memory log = movestore_apply_code; log = log.toSlice().concat(" move store audit success".toSlice()); emit LOG_STRING; return true; } //根据移库申请单查询移库信息记录列表 function getMovestoreLogList(string move_apply_code)public returns (string){ string memory json = "{"; MovestoreWhApply mova = move_applies[move_apply_code]; string[] memory receipt_code_list = mova.movestore_receipt_code_list; for(uint i=0;i<receipt_code_list.length;i++){ WhReceipt wr = wh_receipts[receipt_code_list[i]]; strings.slice[] memory sl = new strings.slice[](26); sl[0] = "\"receipt_code\":\"".toSlice(); sl[1] = wr.receipt_code.toSlice(); sl[2] = "\",\"store_goods_gps\":\"".toSlice(); sl[3] = wr.store_goods_gps.toSlice(); sl[4] = "\"".toSlice(); json = json.toSlice().concatA(sl); if(i!=receipt_code_list.length-1){ json = json.toSlice().concat(",".toSlice()); } } json = json.toSlice().concat( "}".toSlice()); return json; } //冻结仓单 function freezeWhReceipt(string receipt_info)public returns (bool){ string[] memory receipt_code_list = getReceiptCodeList(receipt_info); for(uint i=0;i<receipt_code_list.length;i++){ WhReceipt wr = wh_receipts[receipt_code_list[i]]; if(wr.receipt_status==7||wr.receipt_status==8||wr.receipt_status==2){ wr.receipt_status = 8; } } string memory log = receipt_info; log = log.toSlice().concat(" is freezed success".toSlice()); emit LOG_STRING; return true; } //解冻仓单并更新权属人 function unfreezeWhReceipt(string receipt_info,string receipt_owner)public returns (bool){ string[] memory receipt_code_list = getReceiptCodeList(receipt_info); for(uint i=0;i<receipt_code_list.length;i++){ WhReceipt wr = wh_receipts[receipt_code_list[i]]; if(wr.receipt_status!=8){ continue; } wr.receipt_owner = receipt_owner; if(wr.is_wms_verify){ wr.receipt_status = 7; }else{ wr.receipt_status = 2; } } string memory log = receipt_info; log = log.toSlice().concat(" is unfreezed success".toSlice()); emit LOG_STRING; return true; } //仓单注销 function cancelWhReceipt(string outstore_apply_code)public returns (bool){ OutstoreWhApply outa = out_wh_applies[outstore_apply_code]; string[] memory receipt_code_list = outa.receipt_code_list; bool isCancel = false; for(uint i=0;i<receipt_code_list.length;i++){ WhReceipt wr = wh_receipts[receipt_code_list[i]]; if(wr.receipt_status!=3){ continue; } isCancel = true; wr.receipt_status = 6; } string memory log = outstore_apply_code; if(isCancel){ log = log.toSlice().concat(" is canceled success".toSlice()); }else{ log = log.toSlice().concat(" is canceled failed".toSlice()); } emit LOG_STRING; return isCancel; } //仓单融资申请 function financeWhApply(string finance_wh_code,string receipt_info,string bank_no,string finance_info)public returns (bool){ string[] memory receipt_code_list = getReceiptCodeList(receipt_info); bool canApply = false; for(uint i=0;i<receipt_code_list.length;i++){ WhReceipt wr = wh_receipts[receipt_code_list[i]]; if(wr.receipt_status==2&&!wr.is_wms_verify){ wr.receipt_status = 9; canApply = true; }else if(wr.receipt_status==7&&wr.is_wms_verify){ wr.receipt_status = 9; canApply = true; } } FinanceWhApply memory finance_apply; finance_apply.finance_wh_code = finance_wh_code; finance_apply.receipt_code_list= receipt_code_list; finance_apply.bank_no = bank_no; finance_apply.finance_info = finance_info; finance_applies[finance_wh_code] = finance_apply; string memory log = finance_wh_code; if(canApply){ log = log.toSlice().concat(" is finance apply success".toSlice()); }else{ log = log.toSlice().concat(" is finance apply fail".toSlice()); } emit LOG_STRING; return canApply; } //仓单融资审核 function financeWhAudit(string finance_wh_code,string bank_no,string finance_audit_desc,bool is_pass)public returns (bool){ FinanceWhApply finance_apply = finance_applies[finance_wh_code]; finance_apply.finance_audit_desc = finance_audit_desc; if(finance_apply.finance_wh_code.compare_string("")){ return false; } if(!finance_apply.bank_no.compare_string(bank_no)){ return false; } string[] memory receipt_code_list = finance_apply.receipt_code_list; for(uint i=0;i<receipt_code_list.length;i++){ WhReceipt wr = wh_receipts[receipt_code_list[i]]; if(wr.receipt_status!=9){ continue; } if(is_pass){ wr.bank_no = bank_no; wr.receipt_status = 10; }else{ if(wr.is_wms_verify){ wr.receipt_status = 7; }else{ wr.receipt_status = 2; } } } string memory log = finance_wh_code; log = log.toSlice().concat(" is finance audit success".toSlice()); LOG_STRING; return true; } function uploadImageHash(string image_hash)public returns (bool){ image_hashes[image_hash] = image_hash; return (true); } } library utils{ function ecrecoverSig(bytes32 hash, bytes signature) internal pure returns(identity){ bytes32 r = bytesToBytes32(sliceSig(signature, 0, 32)); bytes32 s = bytesToBytes32(sliceSig(signature, 32, 32)); byte v1 = sliceSig(signature, 64, 1)[0]; uint8 v = uint8(v1) + 27; return ecrecover(hash, v, r, s); } function sliceSig(bytes memory data, uint start, uint len) internal pure returns(bytes){ bytes memory b = new bytes(len); for(uint i = 0; i < len; i++){ b[i] = data[i + start]; } return b; } function bytesToBytes32(bytes memory source) internal pure returns (bytes32 result) { assembly { result := mload(add(source, 32)) } } function bytes32ToString(bytes32 x) internal pure returns (string) { bytes memory bytesString = new bytes(32); uint charCount = 0; uint j; for (j = 0; j < 32; j++) { byte char = byte(bytes32(uint(x) * 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); } function bytes20ToString(bytes20 x) internal pure returns (string) { bytes memory bytesString = new bytes(20); uint charCount = 0; for (uint j = 0; j < 20; j++) { byte char = byte(bytes20(uint(x) * 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); } function bytesToHexString(bytes memory bs) internal pure returns(string) { bytes memory tempBytes = new bytes(bs.length * 2); uint len = bs.length; for (uint i = 0; i < len; i++) { byte b = bs[i]; byte nb = (b & 0xf0) >> 4; tempBytes[2 * i] = nb > 0x09 ? byte((uint8(nb) + 0x37)) : (nb | 0x30); nb = (b & 0x0f); tempBytes[2 * i + 1] = nb > 0x09 ? byte((uint8(nb) + 0x37)) : (nb | 0x30); } return string(tempBytes); } function bytes20ToHexString(bytes20 bs) internal pure returns(string) { //bytes memory bbs = bytes(bs); bytes memory tempBytes = new bytes(bs.length * 2); uint len = bs.length; for (uint i = 0; i < len; i++) { byte b = bs[i]; byte nb = (b & 0xf0) >> 4; tempBytes[2 * i] = nb > 0x09 ? byte((uint8(nb) + 0x37)) : (nb | 0x30); nb = (b & 0x0f); tempBytes[2 * i + 1] = nb > 0x09 ? byte((uint8(nb) + 0x37)) : (nb | 0x30); } return string(tempBytes); } function bytesToHexString(bytes32 bs) internal pure returns(string) { //bytes memory bbs = bytes(bs); bytes memory tempBytes = new bytes(bs.length * 2); uint len = bs.length; for (uint i = 0; i < len; i++) { byte b = bs[i]; byte nb = (b & 0xf0) >> 4; tempBytes[2 * i] = nb > 0x09 ? byte((uint8(nb) + 0x37)) : (nb | 0x30); nb = (b & 0x0f); tempBytes[2 * i + 1] = nb > 0x09 ? byte((uint8(nb) + 0x37)) : (nb | 0x30); } return string(tempBytes); } function uintToString(uint i) internal pure returns (string){ if (i == 0) return "0"; uint j = i; uint length; while (j != 0){ length++; j /= 10; } bytes memory bstr = new bytes(length); uint k = length - 1; while (i != 0){ bstr[k--] = byte(uint8(48) + uint8(i % 10)); i /= 10; } return string(bstr); } function bytes32ToBytes(bytes32 data) internal pure returns (bytes) { bytes memory result = new bytes(32); assembly { mstore(add(result, 32), data) } return result; } function stringToBytes32(string memory source) internal pure returns (bytes32 result) { assembly { result := mload(add(source, 32)) } } function stringToBytes20(string memory source) internal pure returns (bytes20 result) { assembly { result := mload(add(source, 20)) } } function bytes32ArrayToString(bytes32[] data) internal pure returns (string) { bytes memory bytesString = new bytes(data.length * 32); uint urlLength; for (uint i = 0; i< data.length; i++) { for (uint j = 0; j < 32; j++) { byte char = byte(bytes32(uint(data[i]) * 2 ** (8 * j))); if (char != 0) { bytesString[urlLength] = char; urlLength += 1; } } } bytes memory bytesStringTrimmed = new bytes(urlLength); for (i = 0; i < urlLength; i++) { bytesStringTrimmed[i] = bytesString[i]; } return string(bytesStringTrimmed); } function stringToUint(string s) internal pure returns (uint) { bytes memory b = bytes(s); uint result = 0; for (uint i = 0; i < b.length; i++) { // c = b[i] was not needed if (b[i] >= 48 && b[i] <= 57) { result = result * 10 + (uint(b[i]) - 48); // bytes and int are not compatible with the operator -. } } return result; // this was missing } function compare_string(string a, string b) internal returns (bool) { if (bytes(a).length != bytes(b).length) { return false; } else { return keccak256(a) == keccak256(b); } } } 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 self) internal pure returns (slice) { 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 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 self) internal pure returns (slice) { 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 self) internal pure returns (string) { 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 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 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 self, slice 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 self, slice 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 self, slice rune) internal pure returns (slice) { 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 self) internal pure returns (slice 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 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 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 self, slice 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 self, slice needle) internal pure returns (slice) { 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 self, slice 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 self, slice needle) internal pure returns (slice) { 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; } event log_bytemask(bytes32 mask); // 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 self, slice needle) internal pure returns (slice) { 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 self, slice needle) internal pure returns (slice) { 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 self, slice needle, slice token) internal pure returns (slice) { 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 self, slice needle) internal pure returns (slice 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 self, slice needle, slice token) internal pure returns (slice) { 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 self, slice needle) internal pure returns (slice 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 self, slice 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 self, slice 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 self, slice other) internal pure returns (string) { 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; } // added by function concatS(slice self, slice other) internal pure returns (slice) { 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 slice(self._len + other._len, retptr); } // added by function concatA(slice self, slice[] parts) internal pure returns (string) { if (parts.length == 0) return ""; uint length = self._len; uint i; for(i = 0; i < parts.length; i++) length += parts[i]._len; string memory ret = new string(length); uint retptr; assembly { retptr := add(ret, 32) } memcpy(retptr, self._ptr, self._len); retptr += self._len; for(i = 0; i < parts.length; i++) { memcpy(retptr, parts[i]._ptr, parts[i]._len); retptr += parts[i]._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 self, slice[] parts) internal pure returns (string) { if (parts.length == 0) return ""; uint length = self._len * (parts.length - 1); uint i; for(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; } }
0.4.20