基于以太坊的产品溯源方案
溯源,指的是往上游寻找发源的地方,比喻向上寻求历史根源。
这里有一个对商品的溯源需求,实现溯源方案有很多技术方案,无非是db+前端、或者是db+中间件+前端;db也是有很多选择,mysql、sqlserver、oracle、mongodb、redis等等不列举了;中间件也是可选择java、php、c/c++等等;前端就更加多了什么vue、h5、ios、android等等。我这里选择不用中间件,直接db+前端,db使用以太坊、前端使用h5。
补充说明下中间件可以带来很多好处,但这里我为了让这个流程简单清晰就没有使用。
现实现一套产品溯源系统,db使用以太坊、前端使用h5。如下
角色如下(角色功能如其名):
<option>消费者</option>
<option>商店</option>
<option>批发商</option>
<option>生产商</option>
<option>监管部门</option>
合约如下:
pragma solidity ^0.4.18;
contract ProductSystem{
struct Product{//产品生产信息
string name; //产品名称
uint num;//库存量
string key;//批号
uint date;//生产时间
address producer;//生产厂商
}
mapping(uint=>Product) public product; //产品数组
struct Warehouse{//仓库
string name;//产品名称
string key;//批号
uint num;//库存量
uint price;//价格
}
struct Message{//流通信息
address seller;//卖方
address buyer;//买方
uint time;//交易日期
uint num;//数量
string key;//批号
}
struct Role{//角色
string name;//名称
uint typ;//角色类型
uint flag;//可操作标记(0不可操作;1可以操作)
uint ware_key;
uint mess_key;
mapping(uint=>Warehouse) ware;//仓库数据
mapping(uint=>Message) mess;//购买记录
}
struct Role_address{//昵称与地址的映射
string name;
address add;
}
struct Report{//举报信息
address people;//举报人
string key;//产品批号
}
uint product_key;//批号的基准值
uint report_key;//举报信息的基准值
uint public name_address_key;//账户的基准值
mapping(address=>Role) public role;
mapping(uint=>Report) public report;
mapping(uint=>Role_address) public role_address;
event enroll_key(bool key);
event product_production_key(bool key);
event transaction_key(bool key);
event reporting_key(bool key);
event process_account_key(bool key);
event huifu_account_key(bool key);
function Productsystem()public{//初始化
product_key=0;
report_key=0;
name_address_key=0;
}
function name_address(string _name)constant returns(address){
uint i;
for(i=0;i<name_address_key;i++){
if(compare(_name,role_address[i].name)){
return role_address[i].add;
}
}
return;
}
function enroll(string _name,uint _type)public returns(bool)//注册角色
{
uint i;
if(role[msg.sender].flag==1){
emit enroll_key(false);
return false;
}
for(i=0;i<name_address_key;i++){
if(compare(_name,role_address[i].name)){
emit enroll_key(false);
return false;
}
}
role_address[name_address_key].name=_name;
role_address[name_address_key].add=msg.sender;
name_address_key++;
role[msg.sender].name=_name;
role[msg.sender].typ=_type;
role[msg.sender].flag=1;
role[msg.sender].ware_key=0;
role[msg.sender].mess_key=0;
emit enroll_key(true);
return true;
}
/*function product_inquire(uint i)constant public returns(string){
string memory results;
results="name:";
results=strConcat(results,product[i].name);
return results;
}*/
function product_production(string _name,uint _num,string _key,address _producer)public returns(bool){//生产产品
uint w_key;
if(role[_producer].typ!=0||role[_producer].flag!=1){
emit product_production_key(false);
return false;
}
if(_producer!=msg.sender){
emit product_production_key(false);
return false;
}
uint i;
for(i=0;i<product_key;i++){
if(compare(_key,product[i].key)){
emit product_production_key(false);
return false;
}
}
product[product_key].name=_name;
product[product_key].num=_num;
product[product_key].key=_key;
product[product_key].date=block.timestamp;
product[product_key].producer=_producer;
w_key=role[_producer].ware_key;
product_key=product_key+1;
role[_producer].ware[w_key].name=_name;
role[_producer].ware[w_key].key=_key;
role[_producer].ware[w_key].num=_num;
role[_producer].ware[w_key].price=0;
role[_producer].ware_key++;
emit product_production_key(true);
return true;
}
//function product_management_add(address account,Warehouse _ware)public{
// role[account].ware.push(_ware);
//}
function compare(string _a,string _b)returns(bool){//判断字符串是否相等
bytes memory a = bytes(_a);
bytes memory b = bytes(_b);
if (a.length != b.length)
return false;
// @todo unroll this loop
for (uint i = 0; i < a.length; i ++)
if (a[i] != b[i])
return false;
return true;
}
function product_management_inquire(address account,string _name)constant public returns (uint){//监管者查询仓库中某产品的数量
uint sum=0;
uint i;
if(role[msg.sender].typ!=4)
return;
for(i=0;i<role[account].ware_key;i++){
if(compare(_name,role[account].ware[i].name)){
sum+=role[account].ware[i].num;
}
}
return sum;
}
function inquire_product(string _name)constant public returns (uint){//查询本人仓库中某产品的数量
uint sum=0;
uint i;
for(i=0;i<role[msg.sender].ware_key;i++){
if(compare(_name,role[msg.sender].ware[i].name)){
sum+=role[msg.sender].ware[i].num;
}
}
return sum;
}
function transaction(address _from,address to,string _name,string _key,uint _price)public returns(bool){//产品交易函数
uint i;
uint j;
uint w_key;
uint m_key;
if(role[_from].typ==4||role[to].typ==4||role[to].typ==0){
emit transaction_key(false);
return false;
}
if(_from!=msg.sender){
emit transaction_key(false);
return false;
}
if(role[_from].flag==0){
emit transaction_key(false);
return false;
}
for(i=0;i<role[_from].ware_key;i++){
if(compare(_key,role[_from].ware[i].key)){
if(!compare(role[_from].ware[i].name,_name)){
emit transaction_key(false);
return false;
}
w_key=role[to].ware_key;//将交易产品的信息写入买方的仓库
role[to].ware[w_key].name=_name;
role[to].ware[w_key].key=_key;
role[to].ware[w_key].price=_price;
role[to].ware[w_key].num=role[_from].ware[i].num;
for(j=i;j<role[_from].ware_key-1;j++){//从卖方仓库中删除交易产品的信息
role[_from].ware[j].name=role[_from].ware[j+1].name;
role[_from].ware[j].key= role[_from].ware[j+1].key;
role[_from].ware[j].num=role[_from].ware[j+1].num;
role[_from].ware[j].price=role[_from].ware[j+1].price;
}
role[to].ware_key++;
role[_from].ware_key--;
m_key=role[to].mess_key;//交易信息写入买方的交易信息数组
role[to].mess[m_key].seller=_from;
role[to].mess[m_key].buyer=to;
role[to].mess[m_key].time=block.timestamp;
role[to].mess[m_key].num=role[to].ware[w_key].num;
role[to].mess[m_key].key=_key;
role[to].mess_key++;
emit transaction_key(true);
return true;
}
}
emit transaction_key(false);
return false;
}
function strConcat(string _a, string _b) returns (string){//字符串拼接
bytes memory _ba = bytes(_a);
bytes memory _bb = bytes(_b);
string memory ret = new string(_ba.length + _bb.length);
bytes memory bret = bytes(ret);
uint k = 0;
for (uint i = 0; i < _ba.length; i++)bret[k++] = _ba[i];
for (i = 0; i < _bb.length; i++) bret[k++] = _bb[i];
return string(ret);
}
function inquire(address account,string _key)constant public returns (string){//查询产品交易信息链
uint i;
string memory results;
results="";
if(role[account].flag==0){
return;
}
if(0==role[account].typ){
results=role[account].name;
return results;
}
for(i=0;i<role[account].mess_key;i++){
if(compare(_key,role[account].mess[i].key)){
results=inquire(role[account].mess[i].seller,_key);
break;
}
}
if(i==role[account].mess_key){
return "errer!!!";
}
results=strConcat("<=",results);
results=strConcat(role[account].name,results);
return results;
}
function reporting(address _account,string _key)public returns(bool){//消费者提交举报信息
if(_account!=msg.sender){
emit reporting_key(false);
return false;
}
else
{
report[report_key].people=_account;
report[report_key].key=_key;
report_key++;
}
emit reporting_key(true);
return true;
}
function buyer_inquire_mess(uint _int)constant public returns(address,address,uint,uint,string){//返回消费记录
/*uint i;
string memory results;
for(i=0;i<role[_account].mess_key;i++){
results=strConcat(results,"seller:");
results=strConcat(results,role[role[_account].mess[i].seller].name);
results=strConcat(results," ");
results=strConcat(results,"buyer:");
results=strConcat(results,role[role[_account].mess[i].buyer].name);
results=strConcat(results," ");
results=strConcat(results,"key:");
results=strConcat(results,role[_account].mess[i].key);
results=strConcat(results,"\n ");
}*/
if(_int<role[msg.sender].mess_key)
return (role[msg.sender].mess[_int].seller,role[msg.sender].mess[_int].buyer,role[msg.sender].mess[_int].time,role[msg.sender].mess[_int].num,role[msg.sender].mess[_int].key);
else
return;
}
function buyer_inquire_ware(uint _int)constant public returns(string,string,uint,uint){//返回仓库信息
/*uint i;
string memory results;
for(i=0;i<role[_account].ware_key;i++){
results=strConcat(results,"name:");
results=strConcat(results,role[_account].ware[i].name);
results=strConcat(results," ");
results=strConcat(results,"key:");
results=strConcat(results,role[_account].ware[i].key);
results=strConcat(results,"\n ");
}*/
if(_int<role[msg.sender].ware_key)
return (role[msg.sender].ware[_int].name,role[msg.sender].ware[_int].key,role[msg.sender].ware[_int].num,role[msg.sender].ware[_int].price);
else
return;
}
function process_report(address _account)constant public returns(string){//处理举报信息
uint i;
uint j;
string memory results;
if(role[msg.sender].typ!=4)
return;
for(i=report_key-1;i>=0;i--){
if(_account==report[i].people){
results=inquire(report[i].people,report[i].key);
for(j=i;j<report_key-1;j++){
report[j].people=report[j+1].people;
report[j].key=report[j+1].key;
}
report_key--;
break;
}
}
return results;
}
function process_account(address _account)public returns(bool){//处理违规账户
if(role[msg.sender].typ!=4){
emit process_account_key(false);
return false;
}
role[_account].flag=0;
emit process_account_key(true);
return true;
}
function huifu_account(address _account)public returns(bool){//恢复违规账户
if(role[msg.sender].typ!=4){
emit huifu_account_key(false);
return false;
}
role[_account].flag=1;
emit huifu_account_key(true);
return true;
}
}
搭建本地以太坊开发环境,弄一个私链,不清楚搭建看这个网址
1.启动本地环境
Geth输入命令
geth -targetgaslimit 4294967295 -rpc -rpcaddr “127.0.0.1” -rpcport “8101” -port “30301” -rpcapi “eth,web3,personal” -networkid 123 -identity 123 -nodiscover -maxpeers 5 -datadir “/root/ethereum/chain2” -unlock 0 -rpccorsdomain “*” -mine console
其中,datadir表示数据存储位置,123表示识别ID,要保持和其他节点一致,port表示使用的和别的节点连接的端口,rpc表示与外界进行交互,其中暴露的端口rpcport默认为8545也可以修改为自己的8101,rpcdomain表示允许进行访问的网站域名集,*表示所有网站都可以访问,否则会出现无法进行跨域访问的报错,console表示控制台打开。
-
创建账户并解锁账户,挖矿,使账户具有一定的余额
> eth.accounts ["0x4aeb1e38b0821c26d6d834e4e7680301bbbba7f1", "0x2a35fc2bb657e247b10cff2a009b0912d4f9eace", "0x1340037bcc54787b2b629189b4eff3eded6741c5", "0x63cbdd13bf223ce6ce675457155c2198b377b05f", "0x424a7972e9fa609c0834c23e7dfd83a68de8531b", "0x79143c7d4af835e9836276ff8788614865811b04"] > > eth.getBalance(eth.accounts[0]) 1.36757044740584e+23
personal.newcount(“password”);//创建新账户
personal.unlockAccount(eth.account[0]);//解锁账户
miner.start()//开始挖矿
3.发布合约,修改连接文件
使用loadScript(“001.js”);将合约发布,将合约地址复制。
打开项目中的连接文件conect.js,将地址替换为新的合约地址。
INFO [03-30|17:58:14] Regenerated local transaction journal transactions=0 accounts=0
INFO [03-30|18:12:50] Submitted contract creation fullhash=0xdec1c60c8361169a2db4cec9ee9cd8e891e3ea142857ae517b3066fe5f35d7c7 contract=0x4dfA7D24F4913Fbb6869ed2180200e767328573B
INFO [03-30|18:14:03] Updated mining threads threads=0
INFO [03-30|18:14:03] Transaction pool price threshold updated price=18000000000
INFO [03-30|18:14:03] Starting mining operation
INFO [03-30|18:14:03] Commit new mining work number=28153 txs=1 uncles=0 elapsed=2.477ms
前端管理界面就不上代码了,源码见下。
源码地址
本地调试可使用webstorm打开工程或者直接文件夹中找到index.html,双击index.html在浏览器中打开,修改相关配置:
conect.js 文件
if (typeof web3 !== 'undefined')
web3 = new Web3(web3.currentProvider);
else
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8101"));//此处为geth填写的rpcport
if(!web3.isConnected())
console.log("UnConected")
else
console.log("Conected")
deployeAddr="0xA975A87c72A2E4141E85365AFf0424b7da45Ae3A"; //此处为合约发布后的地址
index.js 文件
if (typeof web3 !== 'undefined')
web3 = new Web3(web3.currentProvider);
else
{
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8101"));
}
打开index.html愉快的工作了!个人信息界面
更多推荐
溯源项目(全套源码)
发布评论