实现思路:
首先用地址与每个结构进行映射,将关键信息储存在结构体中;或者将关键信息在外部通过json储存,内部储存对应的hash值;
使用issue函数表示:玉米地中收获足够数量的玉米并进行记录;
使用transfer函数表示:玉米在源产地与经销商手中流转,最终流转至消费者手中;
使用getCornCount函数:查询当前该角色所拥有的玉米数量;
使用IsInHead函数:判断当前该角色是否为玉米源产地;
使用LeafQuery函数:消费者查询玉米的来路,进行溯源操作;
使用NodeQueryFloor函数:经销商查询玉米的去路,进行商品去路调研获取数据,以便后期进行分析;
话不多说,先上代码:
pragma solidity ^0.4.; //注意一些关键原则
//自己不能给自己转玉米:确保不形成自环,且无现实意义;
//每个地址代表一个角色; contract FindCorn {
// 三种关键角色 源产地 经销商与消费者
struct Consumer_Dealer_Origin {
uint count; //当前代表角色玉米总数
//string place; //当前代表角色地理位置信息
//uint begin_time; //当前代表角色获得玉米时刻
//uint end_time; //当前代表角色失去玉米时刻 //以上多点均为玉米溯源过程中所需信息
//可以根据具体需求进行增减
//重点关注整体的框架设计及信息的流转
address father; //连接当前父节点
address[] child; //连接当前节点的子节点
} address[] Head; //存储root节点信息
mapping(address => Consumer_Dealer_Origin) identify; //当前角色与其地址的映射关系 // function GetNewNode(string placename) returns(Consumer_Dealer_Origin) {
// Consumer_Dealer_Origin A = new Consumer_Dealer_Origin({
// count : 0,
// place : "",
// begin_time : 0,
// end_time : 0, // father : '0x00000000' // });
// return A;
// } //收获玉米啦,取到多少算多少
function issue(address input,uint count) returns (string, address, uint) {
identify[input].count = identify[input].count + count;
// identify[input].begin_time = nowtime;
Head.push(input);
return ("add corn success!",input,count);
} //玉米流通啦,卖玉米啦
//地址本身不能进行玉米流通
function transfer(address from1,address to,uint num) returns (string,bool){
if(from1==to){
return ("you can't transfer corn to yourself",false);
}else if(num==){
return ("you can't transfer zero corn to others",false);
}
if(identify[from1].count>=num){
identify[from1].count = identify[from1].count - num;
identify[to].count = identify[to].count + num; //确定玉米流通的流向关系
identify[from1].child.push(to);
identify[to].father = from1; return ("add the corn success!",true);
}
return ("this from1 don't have enough corn!",false);
} //查询账户剩余玉米数
function getCornCount(address query) returns (address,uint){
return (identify[query].father, identify[query].count);
} function IsInHead(address i) returns (bool){
for(uint j=;j < Head.length;j++){
if(Head[j]==i)
return true;
}
return false;
} address []addrpath = new address[]();
//消费者查询:我的玉米从哪里来
function LeafQuery(address consumer) returns (address[]){
addrpath.length = ;
addrpath[addrpath.length++]=consumer;
while(!IsInHead(addrpath[addrpath.length-])){
consumer = identify[addrpath[addrpath.length-]].father;
addrpath[addrpath.length++]=consumer;
}
return addrpath;
} //经销商查询:我的玉米从哪里来
function NodeQueryCeil(address corn) returns (address[]) {
return LeafQuery(corn);
} //经销商查询:玉米去哪了
address []queue = new address[]();
uint index1;
uint index2;
function NodeQueryFloor(address corn) returns (address[]){
//对经销商节点开始进行层次遍历,查找出所有的叶子节点
index1=;
index2=;
addrpath.length = ;
queue.length=;
queue[queue.length++]=corn;
while(index1!=index2){
if(identify[queue[index1]].child.length==){
addrpath[addrpath.length++]=queue[index1];
}
index2 = index2+identify[queue[index1]].child.length;
for(uint i=;i<identify[queue[index1]].child.length;i++){
queue[queue.length++]=identify[queue[index1]].child[i];
}
index1++;
}
return addrpath;
}
}
假设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
修改后完善代码,警告(当前代码的数据可能是仅仅只保存在内存当中的,可能会出现丢失的情况,需要将其完善成固定存储)
pragma solidity ^0.4.; //使用继承的方式书写合约 使得整体逻辑变清晰
contract Corn {
//收获玉米,向源产地对象地址产出的玉米数量进行记录
function harvestCorn(address input, uint count) returns(string, address, uint){} //玉米流通,将玉米在不同角色中流转的数量关系及对象关系进行记录
function transpartCorn(address from1, address to, uint num) returns(string, bool){} //获取当前对象地址的玉米数量
function getCornCount(address query) returns(address, uint){} //判断当前对象是否属于源产地
function isInHead(address i) returns(bool) {} //从当前对象出发查询商品来源
function nodeQueryCeil(address corn) returns(address[]) {} //从当前对象出发查询商品流向
function nodeQueryFloor(address corn) returns(address[], address[]) {} //消费者查询当前商品来源
function leafQuery(address consumer) returns(address[]){}
} contract FindCorn is Corn{
// 三种关键角色 源产地 经销商与消费者
struct Consumer_Dealer_Origin {
uint count; //当前代表角色玉米总数
address father; //连接当前父节点
address[] child; //连接当前节点的子节点
} address[] Head; //存储root节点信息 用来代表分片玉米地
mapping(address => Consumer_Dealer_Origin) identify; //当前角色与其地址的映射关系 //收获玉米啦 对收获的玉米进行数量记录,同时所有收获的玉米属于同一个生产地
function harvestCorn(address input, uint count) returns(string, address, uint) {
identify[input].count = identify[input].count + count;
bool flag = false;
for (uint i = ; i < Head.length; i++) {
if (input == Head[i]) {
flag = true;
break;
}
}
if (!flag) {
Head.push(input);
} return ("add corn success!", input, count);
} //玉米流通啦,卖玉米啦
//地址本身不能进行玉米流通
function transpartCorn(address from1, address to, uint num) returns(string, bool) {
if (from1 == to) {
return ("you can't transfer corn to yourself", false);
} else if (num == ) {
return ("you can't transfer zero corn to others", false);
}
if (identify[from1].count >= num) {
identify[from1].count = identify[from1].count - num;
identify[to].count = identify[to].count + num; //确定玉米流通的流向关系
identify[from1].child.push(to);
identify[to].father = from1; return ("add the corn success!", true);
}
return ("this from1 don't have enough corn!", false);
} //查询账户剩余玉米数
function getCornCount(address query) returns(address, uint) {
return (identify[query].father, identify[query].count);
} //判断当前地址所对应的对象是否属于玉米某片地的角色
function isInHead(address i) returns(bool) {
for (uint j = ; j < Head.length; j++) {
if (Head[j] == i)
return true;
}
return false;
} address[] addrpath = new address[]();
//消费者查询:我的玉米从哪里来
function leafQuery(address consumer) returns(address[]) {
addrpath.length = ;
addrpath[addrpath.length++] = consumer;
while (!isInHead(addrpath[addrpath.length - ])) {
consumer = identify[addrpath[addrpath.length - ]].father;
addrpath[addrpath.length++] = consumer;
}
return addrpath;
} //经销商查询:我的玉米从哪里来
function nodeQueryCeil(address corn) returns(address[]) {
return leafQuery(corn);
} //经销商查询:玉米去哪了
address[] queue = new address[]();
address[] ans = new address[](); function nodeQueryFloor(address corn) returns(address[], address[]) {
//内存化变量初始化
uint index1;
uint index2;
address temp;
//对经销商节点开始进行层次遍历,查找出所有的叶子节点
index1 = ;
index2 = ;
addrpath.length = ;
queue.length = ;
queue[queue.length++] = corn;
while (index1 != index2) {
if (identify[queue[index1]].child.length == ) {
addrpath[addrpath.length++] = queue[index1];
}
index2 = index2 + identify[queue[index1]].child.length;
for (uint i = ; i < identify[queue[index1]].child.length; i++) {
queue[queue.length++] = identify[queue[index1]].child[i];
}
index1++;
} ans.length = ;
for (uint j = ; j < addrpath.length; j++) {
ans.push(addrpath[j]);
temp = addrpath[j];
while (temp != corn && identify[temp].father != corn) {
ans.push(identify[temp].father);
temp = identify[temp].father;
}
ans.push(corn);
}
return (addrpath, ans);
}
}
参考了一位前辈的经验,对于不同类型物品的溯源,需要做到从频率及价值两个维度进行划分;【网名:netkiller,有自制手札】
pragma solidity ^0.4.; //**
// * Author: ZJLavender
// * Date: August 20
// * Update: fix zero address bug
// * Version: 0.9.02
// * Introduction: 玉米合约用于:消费者溯源玉米来源,源产地及经销商追踪玉米流向,同时提供玉米正常流转记录方法,对常见流转场景进行覆盖,更多需求可以基于其上完善
// * /
contract CornTransport { uint256 RETURN_SUCCESS = ; uint256 RETURN_DATAOVERFLOW = ;
uint256 RETURN_FROMTOADDRESSSAME = ;
uint256 RETURN_TRANSPORTCOUNTZERO = ;
uint256 RETURN_CORNCOUNTNOTENOUGH = ;
uint256 RETURN_ILLEGAL_ADDRESS = ; struct Consumer_Dealer_Origin {
uint count;
address addr_from;
address[] addr_to;
} address[] FieldsOfCornAddr;
address[] addrpath;
address[] queue;
address[] ans;
address NULL; mapping(address => Consumer_Dealer_Origin) identify; //functionName: harvestCorn
//input:
// cornFieldAddr address Use address to replace cornField
// count uint256 The number of corn this cornField havest
//return:
// Return_Code uint256 The Result Of invoke harvestCorn
// TheAddress address The input cornFieldAddr
// addrCornSum The Sum of corn this Corn Field have
function harvestCorn(address cornFieldAddr, uint256 count) returns(uint256 Return_Code, address TheAddress, uint256 addrCornSum) {
if(cornFieldAddr == NULL){
return (RETURN_ILLEGAL_ADDRESS, cornFieldAddr, identify[cornFieldAddr].count);
}else if( identify[cornFieldAddr].count + count >= identify[cornFieldAddr].count){
identify[cornFieldAddr].count = identify[cornFieldAddr].count + count;
}else{
return (RETURN_DATAOVERFLOW, cornFieldAddr, identify[cornFieldAddr].count);
} bool flag = false;
for (uint i = ; i < FieldsOfCornAddr.length; i++) {
if (cornFieldAddr == FieldsOfCornAddr[i]) {
flag = true;
break;
}
}
if (!flag)
FieldsOfCornAddr.push(cornFieldAddr);
return (RETURN_SUCCESS, cornFieldAddr, identify[cornFieldAddr].count); } //functionName: transportCorn
//input:
// fromAddr address the address who output corn
// toAddr address the address who input corn
// count uint256 the transport number
//return:
// Return_Code uint256 The Result Of invoke transportCorn
// isSuccess bool The Result Of invoke transportCorn
function transportCorn(address fromAddr, address toAddr, uint256 count) returns(uint256 Return_Code, bool isSuccess) {
if(fromAddr == NULL || toAddr == NULL){
return (RETURN_ILLEGAL_ADDRESS, false);
}else if (fromAddr == toAddr) {
return (RETURN_FROMTOADDRESSSAME, false);
} else if (count == ) {
return (RETURN_TRANSPORTCOUNTZERO, false);
} if (identify[fromAddr].count >= count) {
identify[fromAddr].count = identify[fromAddr].count - count;
identify[toAddr].count = identify[toAddr].count + count; identify[fromAddr].addr_to.push(toAddr);
identify[toAddr].addr_from = fromAddr; return (RETURN_SUCCESS, true);
}
return (RETURN_CORNCOUNTNOTENOUGH, false); } //functionName: getCornCount
//input:
// query address the address query how much corn
//return:
// cornCount uint256 The number of corn count
function getCornCount(address query) returns(uint256 cornCount){
return (identify[query].count);
} //functionName: isInHead
//input:
// isInHeadAddress address query address whether in Head
//return:
// bool address whether in Head
function isInHead(address isInHeadAddress) returns(bool) {
for (uint j = ; j < FieldsOfCornAddr.length; j++) {
if (FieldsOfCornAddr[j] == isInHeadAddress)
return true;
}
return false;
} //functionName: dealer_consumerQuery
//input:
// consumer address input address query where corn from
//return:
// Answer bool invoke Answer
// Return_Code uint256 Return_Code
// address[] the path of where corn from
function dealer_consumerQuery(address consumer) returns(bool Answer, uint256 Return_Code, address[]){
addrpath.length = ;
addrpath[addrpath.length++] = consumer;
while (!isInHead(addrpath[addrpath.length - ])) {
consumer = identify[addrpath[addrpath.length - ]].addr_from;
addrpath[addrpath.length++] = consumer; if(addrpath.length == && addrpath[] == addrpath[]){
return (false, RETURN_ILLEGAL_ADDRESS, addrpath);
}
}
return (true, RETURN_SUCCESS, addrpath);
} //functionName: origin_dealer_QueryCornTo
//input:
// corn address input address query where corn to
//return:
// Answer bool invoke Answer
// Return_Code uint256 Return_Code
// address[] the node of where corn to
// address[] the path of where corn to
function origin_dealer_QueryCornTo(address corn) returns(bool, uint256, address[], address[]){
uint index1;
uint index2;
address temp; index1 = ;
index2 = ;
addrpath.length = ;
queue.length = ;
queue[queue.length++] = corn;
if(identify[corn].addr_to.length == ){
return(false, RETURN_ILLEGAL_ADDRESS, ans, ans);
} while (index1 != index2) {
if (identify[queue[index1]].addr_to.length == ) {
addrpath[addrpath.length++] = queue[index1];
}
index2 = index2 + identify[queue[index1]].addr_to.length;
for (uint i = ; i < identify[queue[index1]].addr_to.length; i++) {
queue[queue.length++] = identify[queue[index1]].addr_to[i];
}
index1++;
} ans.length = ;
for (uint j = ; j < addrpath.length; j++) {
ans.push(addrpath[j]);
temp = addrpath[j];
while (temp != corn && identify[temp].addr_from != corn) {
ans.push(identify[temp].addr_from);
temp = identify[temp].addr_from;
}
ans.push(corn);
} return (true, RETURN_SUCCESS, addrpath, ans);
} }
bug fix 9.02