实现思路:
首先用地址与每个结构进行映射,将关键信息储存在结构体中;或者将关键信息在外部通过json储存,内部储存对应的hash值;
使用issue函数表示:玉米地中收获足够数量的玉米并进行记录;
使用transfer函数表示:玉米在源产地与经销商手中流转,最终流转至消费者手中;
使用getCornCount函数:查询当前该角色所拥有的玉米数量;
使用IsInHead函数:判断当前该角色是否为玉米源产地;
使用LeafQuery函数:消费者查询玉米的来路,进行溯源操作;
使用NodeQueryFloor函数:经销商查询玉米的去路,进行商品去路调研获取数据,以便后期进行分析;
话不多说,先上代码:
1 pragma solidity ^0.4.11; 2 3 //注意一些关键原则 4 //自己不能给自己转玉米:确保不形成自环,且无现实意义; 5 //每个地址代表一个角色; 6 7 contract FindCorn { 8 // 三种关键角色 源产地 经销商与消费者 9 struct Consumer_Dealer_Origin { 10 uint count; //当前代表角色玉米总数 11 //string place; //当前代表角色地理位置信息 12 //uint begin_time; //当前代表角色获得玉米时刻 13 //uint end_time; //当前代表角色失去玉米时刻 14 15 //以上多点均为玉米溯源过程中所需信息 16 //可以根据具体需求进行增减 17 //重点关注整体的框架设计及信息的流转 18 address father; //连接当前父节点 19 address[] child; //连接当前节点的子节点 20 } 21 22 address[] Head; //存储root节点信息 23 mapping(address => Consumer_Dealer_Origin) identify; //当前角色与其地址的映射关系 24 25 // function GetNewNode(string placename) returns(Consumer_Dealer_Origin) { 26 // Consumer_Dealer_Origin A = new Consumer_Dealer_Origin({ 27 // count : 0, 28 // place : "", 29 // begin_time : 0, 30 // end_time : 0, 31 32 // father : '0x00000000' 33 34 // }); 35 // return A; 36 // } 37 38 //收获玉米啦,取到多少算多少 39 function issue(address input,uint count) returns (string, address, uint) { 40 identify[input].count = identify[input].count + count; 41 // identify[input].begin_time = nowtime; 42 Head.push(input); 43 return ("add corn success!",input,count); 44 } 45 46 //玉米流通啦,卖玉米啦 47 //地址本身不能进行玉米流通 48 function transfer(address from1,address to,uint num) returns (string,bool){ 49 if(from1==to){ 50 return ("you can't transfer corn to yourself",false); 51 }else if(num==0){ 52 return ("you can't transfer zero corn to others",false); 53 } 54 if(identify[from1].count>=num){ 55 identify[from1].count = identify[from1].count - num; 56 identify[to].count = identify[to].count + num; 57 58 //确定玉米流通的流向关系 59 identify[from1].child.push(to); 60 identify[to].father = from1; 61 62 return ("add the corn success!",true); 63 } 64 return ("this from1 don't have enough corn!",false); 65 } 66 67 //查询账户剩余玉米数 68 function getCornCount(address query) returns (address,uint){ 69 return (identify[query].father, identify[query].count); 70 } 71 72 73 function IsInHead(address i) returns (bool){ 74 for(uint j=0;j < Head.length;j++){ 75 if(Head[j]==i) 76 return true; 77 } 78 return false; 79 } 80 81 address []addrpath = new address[](1); 82 //消费者查询:我的玉米从哪里来 83 function LeafQuery(address consumer) returns (address[]){ 84 addrpath.length = 0; 85 addrpath[addrpath.length++]=consumer; 86 while(!IsInHead(addrpath[addrpath.length-1])){ 87 consumer = identify[addrpath[addrpath.length-1]].father; 88 addrpath[addrpath.length++]=consumer; 89 } 90 return addrpath; 91 } 92 93 //经销商查询:我的玉米从哪里来 94 function NodeQueryCeil(address corn) returns (address[]) { 95 return LeafQuery(corn); 96 } 97 98 //经销商查询:玉米去哪了 99 address []queue = new address[](1); 100 uint index1; 101 uint index2; 102 function NodeQueryFloor(address corn) returns (address[]){ 103 //对经销商节点开始进行层次遍历,查找出所有的叶子节点 104 index1=0; 105 index2=1; 106 addrpath.length = 0; 107 queue.length=0; 108 queue[queue.length++]=corn; 109 while(index1!=index2){ 110 if(identify[queue[index1]].child.length==0){ 111 addrpath[addrpath.length++]=queue[index1]; 112 } 113 index2 = index2+identify[queue[index1]].child.length; 114 for(uint i=0;i){ 115 queue[queue.length++]=identify[queue[index1]].child[i]; 116 } 117 index1++; 118 } 119 return addrpath; 120 } 121 }
假设0x1地址是玉米地,其中0x2、0x3、0x6都是玉米经销商,0x4、0x5、0x7、0x8都是玉米消费者,那么他们最后的玉米流转关系图如下图中的树关系:
首先执行issue方法,给0x1地址冲入玉米,表示从玉米地收获玉米;
Function [issue] invoking... Invoke args: From 0xca35b7d915458ef540ade6068dfe2f44e8fa733c To 0xf90cfc79dda26f368da31dc0b7944d25ca9a2407 Constant false Payload 867904b400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000002710 Invoke finish Result 0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000002710000000000000000000000000000000000000000000000000000000000000001161646420636f726e207375636365737321000000000000000000000000000000 Decoded ["string: add corn success!","address: 0x1","uint256: 10000"] TxHash 0x86930a394106caf46f2aef7d77772d51a6ba2a555a8a1bd24f148f3200cf23a1 From 0xca35b7d915458ef540ade6068dfe2f44e8fa733c To 0xf90cfc79dda26f368da31dc0b7944d25ca9a2407
然后开始转运玉米,包括:
1到2;1到3;2到4;2到5;3到6;6到7;7到8;
transfer方法:
Function [transfer] invoking... Invoke args: From 0xca35b7d915458ef540ade6068dfe2f44e8fa733c To 0xf90cfc79dda26f368da31dc0b7944d25ca9a2407 Constant false Payload beabacc8000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000064 Invoke finish Result 0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000156164642074686520636f726e2073756363657373210000000000000000000000 Decoded ["string: add the corn success!","bool: true"] TxHash 0x356356817753e1ffba1378f9b0f48e0253f56e7dadb75004c53b156b984a983e From 0xca35b7d915458ef540ade6068dfe2f44e8fa733c To 0xf90cfc79dda26f368da31dc0b7944d25ca9a2407
消费者开始溯源手头的玉米流转流程:
LeafQuery:
Function [LeafQuery] invoking... Invoke args: From 0xca35b7d915458ef540ade6068dfe2f44e8fa733c To 0xf90cfc79dda26f368da31dc0b7944d25ca9a2407 Constant false Payload 210d7dec0000000000000000000000000000000000000000000000000000000000000008 Invoke finish Result 0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001 Decoded ["address[]: 0x8, 0x6, 0x3, 0x1"] TxHash 0xd2019faf0ce53479768ed7564d2394dc2bb95a11f641bdbf93b29b3c9c927689 From 0xca35b7d915458ef540ade6068dfe2f44e8fa733c To 0xf90cfc79dda26f368da31dc0b7944d25ca9a2407
经销商0x3查询玉米去哪里了:
Function [NodeQueryFloor] invoking... Invoke args: From 0xca35b7d915458ef540ade6068dfe2f44e8fa733c To 0xf90cfc79dda26f368da31dc0b7944d25ca9a2407 Constant false Payload ec6d9c640000000000000000000000000000000000000000000000000000000000000003 Invoke finish Result 0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008 Decoded ["address[]: 0x7, 0x8"] TxHash 0xd5d791fb4a3185aba42dfd30ec371a4e7fcfb66127ce13e3f617811c361389c7 From 0xca35b7d915458ef540ade6068dfe2f44e8fa733c To 0xf90cfc79dda26f368da31dc0b7944d25ca9a2407
修改后完善代码,警告(当前代码的数据可能是仅仅只保存在内存当中的,可能会出现丢失的情况,需要将其完善成固定存储)
1 pragma solidity ^0.4.11; 2 3 //使用继承的方式书写合约 使得整体逻辑变清晰 4 contract Corn { 5 //收获玉米,向源产地对象地址产出的玉米数量进行记录 6 function harvestCorn(address input, uint count) returns(string, address, uint){} 7 8 //玉米流通,将玉米在不同角色中流转的数量关系及对象关系进行记录 9 function transpartCorn(address from1, address to, uint num) returns(string, bool){} 10 11 //获取当前对象地址的玉米数量 12 function getCornCount(address query) returns(address, uint){} 13 14 //判断当前对象是否属于源产地 15 function isInHead(address i) returns(bool) {} 16 17 //从当前对象出发查询商品来源 18 function nodeQueryCeil(address corn) returns(address[]) {} 19 20 //从当前对象出发查询商品流向 21 function nodeQueryFloor(address corn) returns(address[], address[]) {} 22 23 //消费者查询当前商品来源 24 function leafQuery(address consumer) returns(address[]){} 25 } 26 27 contract FindCorn is Corn{ 28 // 三种关键角色 源产地 经销商与消费者 29 struct Consumer_Dealer_Origin { 30 uint count; //当前代表角色玉米总数 31 address father; //连接当前父节点 32 address[] child; //连接当前节点的子节点 33 } 34 35 address[] Head; //存储root节点信息 用来代表分片玉米地 36 mapping(address => Consumer_Dealer_Origin) identify; //当前角色与其地址的映射关系 37 38 //收获玉米啦 对收获的玉米进行数量记录,同时所有收获的玉米属于同一个生产地 39 function harvestCorn(address input, uint count) returns(string, address, uint) { 40 identify[input].count = identify[input].count + count; 41 bool flag = false; 42 for (uint i = 0; i < Head.length; i++) { 43 if (input == Head[i]) { 44 flag = true; 45 break; 46 } 47 } 48 if (!flag) { 49 Head.push(input); 50 } 51 52 return ("add corn success!", input, count); 53 } 54 55 //玉米流通啦,卖玉米啦 56 //地址本身不能进行玉米流通 57 function transpartCorn(address from1, address to, uint num) returns(string, bool) { 58 if (from1 == to) { 59 return ("you can't transfer corn to yourself", false); 60 } else if (num == 0) { 61 return ("you can't transfer zero corn to others", false); 62 } 63 if (identify[from1].count >= num) { 64 identify[from1].count = identify[from1].count - num; 65 identify[to].count = identify[to].count + num; 66 67 //确定玉米流通的流向关系 68 identify[from1].child.push(to); 69 identify[to].father = from1; 70 71 return ("add the corn success!", true); 72 } 73 return ("this from1 don't have enough corn!", false); 74 } 75 76 //查询账户剩余玉米数 77 function getCornCount(address query) returns(address, uint) { 78 return (identify[query].father, identify[query].count); 79 } 80 81 82 //判断当前地址所对应的对象是否属于玉米某片地的角色 83 function isInHead(address i) returns(bool) { 84 for (uint j = 0; j < Head.length; j++) { 85 if (Head[j] == i) 86 return true; 87 } 88 return false; 89 } 90 91 address[] addrpath = new address[](1); 92 //消费者查询:我的玉米从哪里来 93 function leafQuery(address consumer) returns(address[]) { 94 addrpath.length = 0; 95 addrpath[addrpath.length++] = consumer; 96 while (!isInHead(addrpath[addrpath.length - 1])) { 97 consumer = identify[addrpath[addrpath.length - 1]].father; 98 addrpath[addrpath.length++] = consumer; 99 } 100 return addrpath; 101 } 102 103 //经销商查询:我的玉米从哪里来 104 function nodeQueryCeil(address corn) returns(address[]) { 105 return leafQuery(corn); 106 } 107 108 109 //经销商查询:玉米去哪了 110 address[] queue = new address[](1); 111 address[] ans = new address[](1); 112 113 function nodeQueryFloor(address corn) returns(address[], address[]) { 114 //内存化变量初始化 115 uint index1; 116 uint index2; 117 address temp; 118 //对经销商节点开始进行层次遍历,查找出所有的叶子节点 119 index1 = 0; 120 index2 = 1; 121 addrpath.length = 0; 122 queue.length = 0; 123 queue[queue.length++] = corn; 124 while (index1 != index2) { 125 if (identify[queue[index1]].child.length == 0) { 126 addrpath[addrpath.length++] = queue[index1]; 127 } 128 index2 = index2 + identify[queue[index1]].child.length; 129 for (uint i = 0; i < identify[queue[index1]].child.length; i++) { 130 queue[queue.length++] = identify[queue[index1]].child[i]; 131 } 132 index1++; 133 } 134 135 ans.length = 0; 136 for (uint j = 0; j < addrpath.length; j++) { 137 ans.push(addrpath[j]); 138 temp = addrpath[j]; 139 while (temp != corn && identify[temp].father != corn) { 140 ans.push(identify[temp].father); 141 temp = identify[temp].father; 142 } 143 ans.push(corn); 144 } 145 return (addrpath, ans); 146 } 147 }
参考了一位前辈的经验,对于不同类型物品的溯源,需要做到从频率及价值两个维度进行划分;【网名:netkiller,有自制手札】
1 pragma solidity ^0.4.10; 2 3 //** 4 // * Author: ZJLavender 5 // * Date: August 20 6 // * Update: fix zero address bug 7 // * Version: 0.9.02 8 // * Introduction: 玉米合约用于:消费者溯源玉米来源,源产地及经销商追踪玉米流向,同时提供玉米正常流转记录方法,对常见流转场景进行覆盖,更多需求可以基于其上完善 9 // * / 10 contract CornTransport { 11 12 uint256 RETURN_SUCCESS = 0; 13 14 uint256 RETURN_DATAOVERFLOW = 10001; 15 uint256 RETURN_FROMTOADDRESSSAME = 10002; 16 uint256 RETURN_TRANSPORTCOUNTZERO = 10003; 17 uint256 RETURN_CORNCOUNTNOTENOUGH = 10004; 18 uint256 RETURN_ILLEGAL_ADDRESS = 10005; 19 20 struct Consumer_Dealer_Origin { 21 uint count; 22 address addr_from; 23 address[] addr_to; 24 } 25 26 address[] FieldsOfCornAddr; 27 address[] addrpath; 28 address[] queue; 29 address[] ans; 30 address NULL; 31 32 mapping(address => Consumer_Dealer_Origin) identify; 33 34 35 //functionName: harvestCorn 36 //input: 37 // cornFieldAddr address Use address to replace cornField 38 // count uint256 The number of corn this cornField havest 39 //return: 40 // Return_Code uint256 The Result Of invoke harvestCorn 41 // TheAddress address The input cornFieldAddr 42 // addrCornSum The Sum of corn this Corn Field have 43 function harvestCorn(address cornFieldAddr, uint256 count) returns(uint256 Return_Code, address TheAddress, uint256 addrCornSum) { 44 if(cornFieldAddr == NULL){ 45 return (RETURN_ILLEGAL_ADDRESS, cornFieldAddr, identify[cornFieldAddr].count); 46 }else if( identify[cornFieldAddr].count + count >= identify[cornFieldAddr].count){ 47 identify[cornFieldAddr].count = identify[cornFieldAddr].count + count; 48 }else{ 49 return (RETURN_DATAOVERFLOW, cornFieldAddr, identify[cornFieldAddr].count); 50 } 51 52 bool flag = false; 53 for (uint i = 0; i < FieldsOfCornAddr.length; i++) { 54 if (cornFieldAddr == FieldsOfCornAddr[i]) { 55 flag = true; 56 break; 57 } 58 } 59 if (!flag) 60 FieldsOfCornAddr.push(cornFieldAddr); 61 return (RETURN_SUCCESS, cornFieldAddr, identify[cornFieldAddr].count); 62 63 } 64 65 //functionName: transportCorn 66 //input: 67 // fromAddr address the address who output corn 68 // toAddr address the address who input corn 69 // count uint256 the transport number 70 //return: 71 // Return_Code uint256 The Result Of invoke transportCorn 72 // isSuccess bool The Result Of invoke transportCorn 73 function transportCorn(address fromAddr, address toAddr, uint256 count) returns(uint256 Return_Code, bool isSuccess) { 74 if(fromAddr == NULL || toAddr == NULL){ 75 return (RETURN_ILLEGAL_ADDRESS, false); 76 }else if (fromAddr == toAddr) { 77 return (RETURN_FROMTOADDRESSSAME, false); 78 } else if (count == 0) { 79 return (RETURN_TRANSPORTCOUNTZERO, false); 80 } 81 82 if (identify[fromAddr].count >= count) { 83 identify[fromAddr].count = identify[fromAddr].count - count; 84 identify[toAddr].count = identify[toAddr].count + count; 85 86 identify[fromAddr].addr_to.push(toAddr); 87 identify[toAddr].addr_from = fromAddr; 88 89 return (RETURN_SUCCESS, true); 90 } 91 return (RETURN_CORNCOUNTNOTENOUGH, false); 92 93 } 94 95 //functionName: getCornCount 96 //input: 97 // query address the address query how much corn 98 //return: 99 // cornCount uint256 The number of corn count 100 function getCornCount(address query) returns(uint256 cornCount){ 101 return (identify[query].count); 102 } 103 104 105 //functionName: isInHead 106 //input: 107 // isInHeadAddress address query address whether in Head 108 //return: 109 // bool address whether in Head 110 function isInHead(address isInHeadAddress) returns(bool) { 111 for (uint j = 0; j < FieldsOfCornAddr.length; j++) { 112 if (FieldsOfCornAddr[j] == isInHeadAddress) 113 return true; 114 } 115 return false; 116 } 117 118 //functionName: dealer_consumerQuery 119 //input: 120 // consumer address input address query where corn from 121 //return: 122 // Answer bool invoke Answer 123 // Return_Code uint256 Return_Code 124 // address[] the path of where corn from 125 function dealer_consumerQuery(address consumer) returns(bool Answer, uint256 Return_Code, address[]){ 126 addrpath.length = 0; 127 addrpath[addrpath.length++] = consumer; 128 while (!isInHead(addrpath[addrpath.length - 1])) { 129 consumer = identify[addrpath[addrpath.length - 1]].addr_from; 130 addrpath[addrpath.length++] = consumer; 131 132 if(addrpath.length == 3 && addrpath[2] == addrpath[1]){ 133 return (false, RETURN_ILLEGAL_ADDRESS, addrpath); 134 } 135 } 136 return (true, RETURN_SUCCESS, addrpath); 137 } 138 139 //functionName: origin_dealer_QueryCornTo 140 //input: 141 // corn address input address query where corn to 142 //return: 143 // Answer bool invoke Answer 144 // Return_Code uint256 Return_Code 145 // address[] the node of where corn to 146 // address[] the path of where corn to 147 function origin_dealer_QueryCornTo(address corn) returns(bool, uint256, address[], address[]){ 148 uint index1; 149 uint index2; 150 address temp; 151 152 index1 = 0; 153 index2 = 1; 154 addrpath.length = 0; 155 queue.length = 0; 156 queue[queue.length++] = corn; 157 if(identify[corn].addr_to.length == 0){ 158 return(false, RETURN_ILLEGAL_ADDRESS, ans, ans); 159 } 160 161 while (index1 != index2) { 162 if (identify[queue[index1]].addr_to.length == 0) { 163 addrpath[addrpath.length++] = queue[index1]; 164 } 165 index2 = index2 + identify[queue[index1]].addr_to.length; 166 for (uint i = 0; i < identify[queue[index1]].addr_to.length; i++) { 167 queue[queue.length++] = identify[queue[index1]].addr_to[i]; 168 } 169 index1++; 170 } 171 172 ans.length = 0; 173 for (uint j = 0; j < addrpath.length; j++) { 174 ans.push(addrpath[j]); 175 temp = addrpath[j]; 176 while (temp != corn && identify[temp].addr_from != corn) { 177 ans.push(identify[temp].addr_from); 178 temp = identify[temp].addr_from; 179 } 180 ans.push(corn); 181 } 182 183 return (true, RETURN_SUCCESS, addrpath, ans); 184 } 185 186 }