實現思路:
首先用地址與每個結構進行映射,將關鍵信息儲存在結構體中;或者將關鍵信息在外部通過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<identify[queue[index1]].child.length;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 }