首页 > 技术文章 > Solidity合约:玉米生产溯源

tianxia2s 2018-07-03 20:34 原文

实现思路:

首先用地址与每个结构进行映射,将关键信息储存在结构体中;或者将关键信息在外部通过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 }
bug fix 9.02

 

推荐阅读