本文节选自电子书《Netkiller Blockchain 手札》
Netkiller Blockchain 手札
文档始创于2018-02-10
版权 © 2018 Netkiller(Neo Chan). All rights reserved.
版权声明
转载请与作者联系,转载时请务必标明文章原始出处和作者信息及本声明。
微信订阅号 netkiller-ebook (微信扫描二维码)QQ:13721218 请注明“读者”QQ群:128659835 请注明“读者”网站:http://www.netkiller.cn
内容摘要
这一部关于区块链开发及运维的电子书。
为什么会写区块链电子书?因为2018年是区块链年。
这本电子书是否会出版(纸质图书)? 不会,因为互联网技术更迭太快,纸质书籍的内容无法实时更新,一本书动辄百元,很快就成为垃圾,你会发现目前市面的上区块链书籍至少是一年前写的,内容已经过时,很多例子无法正确运行。所以我不会出版,电子书的内容会追逐技术发展,及时跟进软件版本的升级,做到内容最新,至少是主流。
这本电子书与其他区块链书籍有什么不同?市面上大部分区块链书籍都是用2/3去讲区块链原理,只要不到 1/3 的干货,干货不够理论来凑,通篇将理论或是大谈特谈区块链行业,这些内容更多是头脑风暴,展望区块链,均无法落地实施。本书与那些书籍完全不同,不讲理论和原理,面向应用落地,注重例子,均是干货。
电子书更新频率?每天都会有新内容加入,更新频率最迟不会超过一周,更新内容请关注 https://github.com/netkiller/netkiller.github.io/commits/master
本文采用碎片化写作,原文会不定期更新,请尽量阅读原文。
http://www.netkiller.cn/blockchain/index.html
您的打赏是我的写作动力:http://www.netkiller.cn/blockchain/donations.html
==============================
33.2. 食品安全溯源案例
下面的方案,同样适合药品安全溯源
33.2.1. 背景
需求是通过区块链跟踪产品,实现产品产地,生产,流通等环节溯源。
需求归纳,需要实现下面几点:
产品具备通用的属性,例如名称,价格,重量,颜色,体积等等
生产销售链条跟踪
涉及环节,农产品的供应链是一个非常复杂的过程,涉及多方,农业局、卫生局、药监局、工商局、环保局等多个部门交织其中。
参与者角色,我们为每个环节的参与者分配一个以太坊账号,例如每个供应商一个账号,每个代理商一个账号。这样任何一方经手后都会使用自己的账号想合约中添加数据。
33.2.2. 安全问题
我将安全划分为六层,分别是:
+———-+—————————–+
| 实体层 | 物 |
+———-+—————————–+
| 用户层 | 人 |
+———-+—————————–+
| 网络层 | 网络 |
+———-+—————————–+
| 应用层 | 操作系统,应用服务器 |
+———-+—————————–+
| 业务逻辑层 | 功能,业务逻辑 |
+———-+—————————–+
| 存储层 | 物理存储,硬盘 |
+———-+—————————–+
并不是实施了区块链技术就安全无忧了,安全分为很多层,区块链只能做到网络层和存储层的安全。区块链无法解决用户层,应用层,逻辑层等安全问题,他只能保证存储在硬盘上的区块不被修改。
因为区块链仅仅能解决数据存储层的安全问题,不能保证上链的数据是真实的,上链前绝对不会被篡改;所以仅仅朔源,不考虑防伪是没有意义的,防伪仍然是重中之重。
33.2.3. 防伪问题
如何做防伪呢,这个领域很多公司已经探索多年,各种高科技应用,武装到牙齿,但仍没有解决假货问题。
区块链的出现很可能是一个突破,我们只需将现有成熟的防伪技术与区块链结合即可。
现在流行的访问技术太多了,我倾向于采用二维码技术,二维码与互联网紧密相连。
33.2.4. 性能问题
区块链目前的底层只适合做,低频高价值的业务。
区块链的读取性能通常是没有问题的,但是区块链的写入实际上无论你用多少个服务器节点都不能提升,因为写入区块需要做共识算法,这步操作,会在所有节点上进行,同时还需要加密运算,这些操作都是 CPU 密集型操作。所以写入操作是存在瓶颈的。
解决这个问题,我想出了几种方案:
性能解决方案
为了达到去中心化并行写入,我们将在客户端通过算法,匹配服务器。而不是在两个平台前面增加负载均衡。因为这样又回到了中心化系统。
33.2.5. 颗粒度问题
朔源的颗粒度问题,例如“红酒”的溯源,我们是将单位溯源做到箱呢?还是打,或是瓶呢?
我们用“四象限法则”分析
高价值
o |
| o
|
低频率 ————–+————- 高频率 操作频率
|
o | o
|
低价值
物品价值
通过观察上面图,我们可以看到可以有四种情况,低频低价值,低频高价值,高频高价值,高频低价值
我认为对于低频高价值和高频高价值的业务,尽量做到最小颗粒度。
而对于低频低价值和高频低价值的业务,可以颗粒度更粗。
33.2.6. 存储规划
如果是高频低价值的业务,那么溯源数据源源将会不断的被添加到区块,以此同时区块的访问率极低。迟早会达到一个临界值。
所以你要规划存储,例如溯源数据的过期时间,对于 hyperledger 可以使用 DelState(key) 删除历史数据。
如果是高频高价值的业务是否要考虑永久保留数据呢?
这些问题都是需要考虑的。因为目前我们还不知道区块链的存储临界值。
33.2.7. 大数据问题
区块链替代不了数据库,它与数据库是互补关系。
对于低频的业务,通常传统数据库足以应付。那么对于高频操作的业务呢?暂时可能没有问题,但总有一天会遇到瓶颈。
综上所述,溯源项目数据库规划决不能少。同时还要考虑数据仓库和后期数据挖掘。因为用户使用微信或者我们的APP扫描二维码,我们可以获得很多有价值的数据。
手上没有 Vision 使用文本简单的绘制了一幅图
+————————+
| User -> QR Code |
+————————+
| |
V V
+—————+ +—————+ +—————+
| Search Engine |<&#8211; | Microservice | | Microservice |
+&#8212;&#8212;&#8212;&#8212;&#8212;+ +&#8212;&#8212;&#8212;&#8212;&#8212;+ +&#8212;&#8212;&#8212;&#8212;&#8212;+
| |
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+ |
| | | |
V V V V
+&#8212;&#8212;&#8212;-+ +&#8212;&#8212;&#8212;&#8212;+ +&#8212;&#8212;&#8212;&#8212;-+
| Database | | Big Data | | Blockchain |
+&#8212;&#8212;&#8212;-+ +&#8212;&#8212;&#8212;&#8212;+ +&#8212;&#8212;&#8212;&#8212;-+
| MySQL | | Hadoop | | Hyperledger |
| NoSQL | | Hive/Hbase | | Chaincode |
+&#8212;&#8212;&#8212;-+ +&#8212;&#8212;&#8212;&#8212;+ +&#8212;&#8212;&#8212;&#8212;-+
| | ^ ^
| +&#8212;&#8212; ETL &#8212;&#8211;| |
| |
+&#8212;&#8212;&#8212;&#8211; Message Queue &#8212;&#8212;&#8212;-o
区块链之外的很多复杂的需求我们需要借助大数据系统和搜索技术。
区块链的弱点是无法做复杂的查询,这里我们会用到搜索引擎技术解决,实际上搜索引擎角色是给区块链做索引。
上图数据写入时,保存了四份,分别在搜索引擎,关系型数据库,数据仓库和区块的
具体怎么实现,有很多方式,这里就不讨论了,否则就跑题了。
33.2.8. BI商业智能
数据采集,大数据分析
溯源信息的查询是通过用户手机终端实现,有几种途径,微信扫二维码,APP扫二维码,微信小程序等等。
我们可以收集到很多有价值的数据,例如地理位置,手机号码,性别,年龄等等&#8230;&#8230;
有了这些数据便可以挖掘出有价值的数据,甚至可以将数据提供给生产企业作参考。
大数据能做什么?
33.2.9. 采集终端
溯源数据怎么录入呢?例如我们开发一个设备,二维码扫描枪,内置安卓系统。
我们不清楚他们的教育背景以及学习能力,所以设计原则是尽量傻瓜化,降低数据录入难度和学习难度,终端开机后互动教学,走一遍流程即可上手。
首先将溯源环节的每个节点通过后台事先写入数据库,接下来通过GIS地理信息系统匹配。
UUID -> 二维码 -> 设备扫描二维码激活-> 入数据库 -> 异步消息队列 -> 上链 > &#8212;+
^ |
| |
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- 追加数据 &#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+
终端会帮助用户欲录入信息,用户可以在信息基础上修改或者重写。同时终端支持图片,图像记录上传。
对于图片还能实现 EXIF 数据保存,包括图片描述信息,地理信息等等&#8230;&#8230;
33.2.10. 多媒体数据
这里我们需要考虑是否需要记录多媒体数据,这里的多媒体指图像,声音,甚至3D扫描数据等等&#8230;&#8230;
对于图片、音频与视频,我们可以将它集成到采集终端上,然后异步上传到去中心化的分布式文件系统中。
去中心化的分布式文件系统能实现,一张图片一个hash值,通过hash值访问图片,图片被同步到相邻节点实现去中心化,图片被修改hash值随之变化数据便无效。
33.2.11. 物流接口
使用物流单好通过物流公司提供的借口获得物流数据,然后写入到区块。
33.2.12. 如何激励用户
防伪技术做了,区块链溯源也做了,那么对于用户来说,他可能懒得去扫你的二维码,怎么办呢?
这里需要激励用户,怎样激励用户,我的方案是送代币。
首先代币不仅能够购买物品,还能交易,流通,形成一个小的商业闭环。其次目前代币已经泛滥 99% 可能是空气币,这里我们需要将代币的价值与物品对价,类似金本位/银本位。
怎样操作呢?例如一个代币等于一斤水果,无论代币怎样炒作,最终用户不想玩下去了,就来换水果,也可以是大米,食用油等等&#8230;
关于怎样使用代币来做积分系统请参考我的另一篇文章 《使用代币替代传统积分系统》 ,你可以在搜索引擎中找到
根据业务需要,可以发行布置一套币,例如水果币,流量币,话费币,每种币的功能不同,这些币可以在交易所中撮合交易,例如卖出水果币,换成流量币等等。
由于国家的法规问题,代币系统设计原则一定是代币只能用来购买商城中的物品,不能直接兑换成RMB,否则会触碰到国家的红线。但是通过交易所,币币之间兑换我们就控制不了了。
另外扫描二维码显示溯源防伪信息的同时我们有很多可以操作空间,可以获取用户地理位置,手机号码等等信息,为后面大数据分析埋点。
用户激励手段
等等,手段众多,目的是让用户查询溯源信息,手机用户数据,鼓励代币消费等等&#8230;&#8230;.
33.2.13. 上链
并不是所有数据都上链,哪些数据上链呢?
产地(出生、生长)、采购、加工(检疫、屠宰)、库存、运输、销售、配送等等&#8230;&#8230;
33.2.14. 以太坊解决方案
我们设计一个简单的合约,模拟上面提到的解决方案
pragma solidity ^0.4.20;
contract Trace {
enum State { Origin, Factory, QA, Shipping, Received, Pending }
string name;
uint price;
uint weight;
bool lock = false; //合约锁
bool close = false; //合约状态
uint number = 1;
uint attr_number = 1;
mapping (address => string) guestbook; //客户留言本
struct Attribute {
address owner; // 供应商
string name; // 属性的名字
string date; // 生产日期
string desc; // 描述信息
}
mapping (uint => Attribute) attribute;
struct Logistics {
address owner; // 中转站
string date; // 转运日期
State status; // 状态
string message; // 留言信息
}
mapping (uint => Logistics) stations;
function Trace(string _name, uint _price, uint _weight) public {
name = _name;
price = _price;
weight = _weight;
}
// 名称
function getName() public view returns(string){
return name;
}
// 价格
function getPrice() public view returns(uint){
return price;
}
// 重量
function getWeight() public view returns(uint){
return weight;
}
// 增加商品属性
function putAttribute(address _owner,string _name, string _date, string _desc ) public{
if(lock == false){
Attribute memory item = Attribute(_owner, _name,_date,_desc);
attribute[attr_number] = item;
attr_number = attr_number + 1;
}
}
// 获得属性
function getAttribute(uint _attr_number) public view returns(address, string, string, string) {
require(_attr_number
return (item.owner, item.name, item.date, item.desc);
}
// 增加物流中转信息
function putLogistics(address _owner,string _date, State _status, string _message ) public{
if(close == false){
Logistics memory node = Logistics(_owner,_date,_status,_message);
stations[number] = node;
number = number + 1;
lock = true;
}
if (_status == State.Received) {
close = true;
}
}
// 获得中转信息 Logistics memory node = stations[_number]; return (node.owner, node.date, node.status, node.message); // 或者转中站数量 // 客户留言 Trace(string _name, uint _price, uint _weight) 33.2.14.1. 应用场景一 调用合约案例一,这是没有经过深加工的原产品案例。例如 Trace(&#8220;山羊肉&#8221;, 25, 50) var contract; contract.putLogistics(&#8220;0x627306090abab3a6e1400e9345bc60c78a8bef57&#8243;,&#8221;2018-02-20&#8243;,0,&#8221;XXX牧场&#8221;); contract.getNode(); // 获得物流经过的转运站数量 33.2.14.2. 应用场景二 调用合约案例二,这是深加工的产品案例。例如 Trace(&#8220;牦牛肉干&#8221;, 80, 500) var contract; contract.putLogistics(&#8220;0x627306090abab3a6e1400e9345bc60c78a8bef57&#8243;,&#8221;2018-02-20&#8243;,0,&#8221;XXX牧场&#8221;); contract.getNode(); // 获得物流经过的转运站数量 33.2.14.3. 用户留言 contract.addGuestbook(&#8220;0x0d1d423e623d10f9d10f9d10f9d10f9d10f9fba5&#8243;,&#8221;东西好吃,下次还买,给好评&#8221;); 33.2.15. Hyperledger 解决方案 由于家里在刷墙,服务器收起来了,没有开发环境,只能提供部分参考代码,无法提供合约完整代码,只是给大家一个思路,原理很上面以太坊的合约类似。 33.2.15.1. 溯源合约涉及 package main import &#8220;fmt&#8221; const ( type structElement struct { type structLogistics struct { type Trace struct { func (trace *Trace) setName(_name string) { func (trace *Trace) getName() string { func (trace *Trace) putAttribute(_key string, _value string) { func (trace *Trace) putLogistics(_key string, _value structLogistics) { func main(){ trace := &Trace{ trace.putAttribute(&#8220;Color&#8221;,&#8221;Red&#8221;) trace.putLogistics(&#8220;1&#8221;, structLogistics{&#8220;呼和浩特&#8221;,&#8221;2016-10-15&#8243;, Origin, &#8220;牦牛收购&#8221;}) traceJson, _ := json.Marshal(trace) } trace := &Trace{ trace.putAttribute(&#8220;Color&#8221;,&#8221;Red&#8221;) trace.putLogistics(&#8220;1&#8221;, structLogistics{&#8220;呼和浩特&#8221;,&#8221;2016-10-15&#8243;, Origin, &#8220;牦牛收购&#8221;}) 33.2.15.1.2. 水平移植 这个方案可以水平移植到其他领域,例如 药品安全溯源 trace := &Trace{ trace.putAttribute(&#8220;Color&#8221;,&#8221;Red&#8221;) trace.putLogistics(&#8220;1&#8221;, structLogistics{&#8220;呼和浩特&#8221;,&#8221;2016-10-15&#8243;, Origin, &#8220;原材料&#8230;.&#8221;}) 33.2.15.2. 积分通正(代币) 我发现用以太坊思维,将以太坊代币合约搬到 hyperledger 上,一样可以实现代币的功能,这个代币除了不能上交易所,基本满足我们替代积分系统的需求,下面是我写了这样一个合约,在超级账本上实现类似以太坊的代币转账功能。 package main import ( &#8220;http://github.com/hyperledger/fabric/core/chaincode/shim&#8220; // Define the Smart Contract structure type Token struct { func (token *Token) initialSupply(){ func (token *Token) transfer (_from string, _to string, _value uint){ func (token *Token) balance (_from string) uint{ func (token *Token) burn(_value uint) { func (token *Token) burnFrom(_from string, _value uint) { func (token *Token) mint(_value uint) { token.BalanceOf[token.Owner] += _value; } func (s *SmartContract) Init(stub shim.ChaincodeStubInterface) sc.Response { func (s *SmartContract) initLedger(stub shim.ChaincodeStubInterface) sc.Response { token := &Token{ token.initialSupply() tokenAsBytes, _ := json.Marshal(token) return shim.Success(nil) func (s *SmartContract) transferToken(stub shim.ChaincodeStubInterface, args []string) sc.Response { if len(args) != 3 { tokenAsBytes, _ := stub.GetState(args[0]) json.Unmarshal(tokenAsBytes, &token) tokenAsBytes, _ = json.Marshal(token) return shim.Success(nil) func (s *SmartContract) balanceToken(stub shim.ChaincodeStubInterface, args []string) sc.Response { if len(args) != 1 { tokenAsBytes, _ := stub.GetState(args[0]) json.Unmarshal(tokenAsBytes, &token) return shim.Success(amount) func (s *SmartContract) Invoke(stub shim.ChaincodeStubInterface) sc.Response { // Retrieve the requested Smart Contract function and arguments return shim.Error(&#8220;Invalid Smart Contract function name.&#8221;) // The main function is only relevant in unit test mode. Only included here for completeness. // Create a new Smart Contract
function getLogistics(uint _number) public view returns(address, string, State, string) {
require(_number
}
function getLogisticsCount() public view returns(uint){
return number;
}
function addGuestbook(address _owner, string message) public{
guestbook[_owner] = message;
}
}怎样使用这个合约呢?合约部署,需要输入三个参数,分别是名称,价格和装量
产品属性可以在出厂前设置,一旦出厂进入物流阶段就不允许在更改了。
Trace.deployed().then(function(instance){cOntract=instance;});
contract.getName();
contract.putAttribute(&#8220;0x627306090abab3a6e1400e9345bc60c78a8bef57&#8243;,&#8221;颜色&#8221;, &#8220;&#8221;, &#8220;黑色&#8221;)
contract.putAttribute(&#8220;0x627306090abab3a6e1400e9345bc60c78a8bef57&#8243;,&#8221;产地&#8221;, &#8220;&#8221;, &#8220;内蒙古&#8221;)
contract.putAttribute(&#8220;0x627306090abab3a6e1400e9345bc60c78a8bef57&#8243;,&#8221;出生&#8221;, &#8220;2017-01-12&#8221;, &#8220;XXX牧场&#8221;)
contract.putAttribute(&#8220;0x627306090abab3a6e1400e9345bc60c78a8bef57&#8243;,&#8221;宰杀&#8221;, &#8220;2018-02-12&#8221;, &#8220;XXX宰杀&#8221;)
contract.putLogistics(&#8220;0x627306090abab3a6e1400e9345bc60c78a8bef57&#8243;,&#8221;2018-02-20&#8243;,1,&#8221;XXX屠宰公司&#8221;);
contract.putLogistics(&#8220;0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef&#8221;,&#8221;2018-02-22&#8243;,2,&#8221;XXX检验检疫&#8221;);
contract.putLogistics(&#8220;0xf17f52151ebef6c7334fad080c5704d77216b732&#8243;,&#8221;2018-02-21&#8243;,3,&#8221;XXX一级经销商&#8221;);
contract.putLogistics(&#8220;0x821aea9a577a9b44299b9c15c88cf3087f3b5544&#8243;,&#8221;2018-02-23&#8243;,3,&#8221;XXX二级经销商&#8221;);
contract.putLogistics(&#8220;0x821aea9a577a9b44299b9c15c88cf3087f3b5544&#8243;,&#8221;2018-02-24&#8243;,3,&#8221;XXX批发中心&#8221;);
contract.putLogistics(&#8220;0x821aea9a577a9b44299b9c15c88cf3087f3b5544&#8243;,&#8221;2018-02-25&#8243;,3,&#8221;XXX超市&#8221;);
contract.putLogistics(&#8220;0x0d1d4e623d10f9fba5db95830f7d3839406c6af2&#8243;,&#8221;2018-02-26&#8243;,4,&#8221;用户包裹收到&#8221;);
Trace.deployed().then(function(instance){cOntract=instance;});
contract.getName();
contract.putAttribute(&#8220;0x627306090abab3a6e1400e9345bc60c78a8bef57&#8243;,&#8221;调和油&#8221;, &#8220;2016-10-10&#8221;, &#8220;银龙鱼牌&#8221;)
contract.putAttribute(&#8220;0x627306090abab3a6e1400e9345bc60c78a8bef57&#8243;,&#8221;辣椒粉&#8221;, &#8220;2016-10-30&#8221;, &#8220;西藏XXX公司生产&#8221;)
contract.putAttribute(&#8220;0x627306090abab3a6e1400e9345bc60c78a8bef57&#8243;,&#8221;生抽&#8221;, &#8220;2016-01-12&#8221;, &#8220;XXX生抽,XXX生产&#8221;)
contract.putAttribute(&#8220;0x627306090abab3a6e1400e9345bc60c78a8bef57&#8243;,&#8221;山梨酸钾&#8221;, &#8220;2017-02-12&#8221;, &#8220;XXX生产&#8221;)
contract.putAttribute(&#8220;0x627306090abab3a6e1400e9345bc60c78a8bef57&#8243;,&#8221;防腐剂&#8221;, &#8220;2017-02-12&#8221;, &#8220;XXX生产&#8221;)
contract.putAttribute(&#8220;0x627306090abab3a6e1400e9345bc60c78a8bef57&#8243;,&#8221;牦牛肉&#8221;, &#8220;2017-02-12&#8221;, &#8220;XXX牧场&#8221;)
contract.putLogistics(&#8220;0x627306090abab3a6e1400e9345bc60c78a8bef57&#8243;,&#8221;2018-02-20&#8243;,1,&#8221;XXX公司生产&#8221;);
contract.putLogistics(&#8220;0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef&#8221;,&#8221;2018-02-22&#8243;,2,&#8221;XXX通过QA、QC&#8221;);
contract.putLogistics(&#8220;0xf17f52151ebef6c7334fad080c5704d77216b732&#8243;,&#8221;2018-02-21&#8243;,3,&#8221;XXX一级经销商&#8221;);
contract.putLogistics(&#8220;0x821aea9a577a9b44299b9c15c88cf3087f3b5544&#8243;,&#8221;2018-02-23&#8243;,3,&#8221;XXX二级经销商&#8221;);
contract.putLogistics(&#8220;0x821aea9a577a9b44299b9c15c88cf3087f3b5544&#8243;,&#8221;2018-02-24&#8243;,3,&#8221;XXX批发中心&#8221;);
contract.putLogistics(&#8220;0x821aea9a577a9b44299b9c15c88cf3087f3b5544&#8243;,&#8221;2018-02-25&#8243;,3,&#8221;XXX超市&#8221;);
contract.putLogistics(&#8220;0x0d1d4e623d10f9fba5db95830f7d3839406c6af2&#8243;,&#8221;2018-02-26&#8243;,4,&#8221;用户包裹收到&#8221;);
import &#8220;encoding/json&#8221;
Origin = iota // 0
Factory // 1
QA // 2
Shipping // 3
Received // 4
Pending // 5
Supermarket // 6
)
Name string `json:&#8221;name&#8221;`
Company string `json:&#8221;company&#8221;`
Description string `json:&#8221;description&#8221;`
}
Stations string `json:&#8221;stations&#8221;` // 中转站
Date string `json:&#8221;date&#8221;` // 转运日期
Status uint8 `json:&#8221;status&#8221;` // 状态
Message string `json:&#8221;message&#8221;` // 留言信息
}
Name string `json:&#8221;name&#8221;`
Address string `json:&#8221;address&#8221;`
Attribute map[string]string `json:&#8221;attribute&#8221;`
Element []structElement `json:&#8221;element&#8221;`
Logistics map[string]structLogistics `json:&#8221;logistics&#8221;`
}
trace.Name = _name
}
return trace.Name
}
trace.Attribute[_key] = _value
}
trace.Logistics[_key] = _value
}
Name: &#8220;牦牛肉干&#8221;,
Address: &#8220;内蒙古呼和浩特&#8221;,
Attribute: map[string]string{},
Element: []structElement{structElement{Name:&#8221;塑料袋&#8221;,Company: &#8220;XXX塑料制品有限公司&#8221;, Description: &#8220;外包装&#8221;},structElement{Name:&#8221;辣椒粉&#8221;,Company: &#8220;XXX调味品有限公司&#8221;, Description: &#8220;采摘年份2016-10-10&#8243;},structElement{Name:&#8221;调和油&#8221;,Company: &#8220;XXX调味品有限公司&#8221;, Description: &#8220;生产日期2016-10-10&#8221;}},
Logistics: map[string]structLogistics{}}
trace.putAttribute(&#8220;Size&#8221;,&#8221;10&#8243;)
trace.putAttribute(&#8220;Weight&#8221;,&#8221;100kg&#8221;)
trace.putLogistics(&#8220;2&#8221;, structLogistics{&#8220;呼和浩特&#8221;,&#8221;2016-10-18&#8243;, Factory, &#8220;牦牛宰杀&#8221;})
trace.putLogistics(&#8220;3&#8221;, structLogistics{&#8220;呼和浩特&#8221;,&#8221;2016-10-15&#8243;, QA, &#8220;经过质检&#8221;})
trace.putLogistics(&#8220;4&#8221;, structLogistics{&#8220;北京市&#8221;,&#8221;2016-10-15&#8243;, Shipping, &#8220;运输中&#8221;})
trace.putLogistics(&#8220;5&#8221;, structLogistics{&#8220;杭州市&#8221;,&#8221;2016-10-15&#8243;, Shipping, &#8220;XXX冷库&#8221;})
trace.putLogistics(&#8220;5&#8221;, structLogistics{&#8220;深圳市&#8221;,&#8221;2016-10-15&#8243;, Supermarket, &#8220;XXX超市&#8221;})
trace.putLogistics(&#8220;5&#8221;, structLogistics{&#8220;龙华区&#8221;,&#8221;2016-10-15&#8243;, Received, &#8220;用户签收&#8221;})
fmt.Println(string(traceJson))33.2.15.1.1. 食品安全朔源
Name: &#8220;牦牛肉干&#8221;,
Address: &#8220;内蒙古呼和浩特&#8221;,
Attribute: map[string]string{},
Element: []structElement{structElement{Name:&#8221;塑料袋&#8221;,Company: &#8220;XXX塑料制品有限公司&#8221;, Description: &#8220;外包装&#8221;},structElement{Name:&#8221;辣椒粉&#8221;,Company: &#8220;XXX调味品有限公司&#8221;, Description: &#8220;采摘年份2016-10-10&#8243;},structElement{Name:&#8221;调和油&#8221;,Company: &#8220;XXX调味品有限公司&#8221;, Description: &#8220;生产日期2016-10-10&#8221;}},
Logistics: map[string]structLogistics{}}
trace.putAttribute(&#8220;Size&#8221;,&#8221;10&#8243;)
trace.putAttribute(&#8220;Weight&#8221;,&#8221;100kg&#8221;)
trace.putLogistics(&#8220;2&#8221;, structLogistics{&#8220;呼和浩特&#8221;,&#8221;2016-10-18&#8243;, Factory, &#8220;牦牛宰杀&#8221;})
trace.putLogistics(&#8220;3&#8221;, structLogistics{&#8220;呼和浩特&#8221;,&#8221;2016-10-15&#8243;, QA, &#8220;经过质检&#8221;})
trace.putLogistics(&#8220;4&#8221;, structLogistics{&#8220;北京市&#8221;,&#8221;2016-10-15&#8243;, Shipping, &#8220;运输中&#8221;})
trace.putLogistics(&#8220;5&#8221;, structLogistics{&#8220;杭州市&#8221;,&#8221;2016-10-15&#8243;, Shipping, &#8220;XXX冷库&#8221;})
trace.putLogistics(&#8220;5&#8221;, structLogistics{&#8220;深圳市&#8221;,&#8221;2016-10-15&#8243;, Supermarket, &#8220;XXX超市&#8221;})
trace.putLogistics(&#8220;5&#8221;, structLogistics{&#8220;龙华区&#8221;,&#8221;2016-10-15&#8243;, Received, &#8220;用户签收&#8221;})
Name: &#8220;强身大力丸&#8221;,
Address: &#8220;深圳是XXX制药有限公司&#8221;,
Attribute: map[string]string{},
Element: []structElement{
structElement{Name:&#8221;枸杞&#8221;,Company: &#8220;宁夏XXX农业有限公司&#8221;, Description: &#8220;采摘年份2016-10-10,10g&#8221;},
structElement{Name:&#8221;茯苓&#8221;,Company: &#8220;河南XXX农业有限公司&#8221;, Description: &#8220;采摘年份2016-10-10,20kg&#8221;},
structElement{Name:&#8221;XXX&#8221;,Company: &#8220;XXX有限公司&#8221;, Description: &#8220;生产日期2016-10-10&#8221;},
structElement{Name:&#8221;XXX&#8221;,Company: &#8220;XXX有限公司&#8221;, Description: &#8220;生产日期2016-10-10&#8221;},
&#8230;
&#8230;
structElement{Name:&#8221;塑料包装&#8221;,Company: &#8220;XXX有限公司&#8221;, Description: &#8220;生产日期2016-10-10&#8221;},
structElement{Name:&#8221;包装盒&#8221;,Company: &#8220;XXX有限公司&#8221;, Description: &#8220;生产日期2016-10-10&#8221;}
},
Logistics: map[string]structLogistics{}}
trace.putAttribute(&#8220;Size&#8221;,&#8221;10&#8243;)
&#8230;
&#8230;
trace.putAttribute(&#8220;Weight&#8221;,&#8221;100kg&#8221;)
trace.putLogistics(&#8220;2&#8221;, structLogistics{&#8220;呼和浩特&#8221;,&#8221;2016-10-18&#8243;, Factory, &#8220;生产&#8230;.&#8221;})
trace.putLogistics(&#8220;3&#8221;, structLogistics{&#8220;呼和浩特&#8221;,&#8221;2016-10-15&#8243;, QA, &#8220;经过质检&#8221;})
trace.putLogistics(&#8220;3&#8221;, structLogistics{&#8220;XXX市药品监督局&#8221;,&#8221;2016-10-15&#8243;, QA, &#8220;经过质检&#8221;})
trace.putLogistics(&#8220;4&#8221;, structLogistics{&#8220;北京市&#8221;,&#8221;2016-10-15&#8243;, Shipping, &#8220;运输中&#8221;})
trace.putLogistics(&#8220;5&#8221;, structLogistics{&#8220;杭州市&#8221;,&#8221;2016-10-15&#8243;, Shipping, &#8220;XXX冷库&#8221;})
trace.putLogistics(&#8220;5&#8221;, structLogistics{&#8220;深圳市&#8221;,&#8221;2016-10-15&#8243;, Supermarket, &#8220;XXX超市&#8221;})
trace.putLogistics(&#8220;5&#8221;, structLogistics{&#8220;龙华区&#8221;,&#8221;2016-10-15&#8243;, Received, &#8220;用户签收&#8221;})合约落地,还需要做一些调整已适应实际场景。但基本思路是通的。
&#8220;bytes&#8221;
&#8220;encoding/json&#8221;
&#8220;fmt&#8221;
&#8220;strconv&#8221;
sc &#8220;http://github.com/hyperledger/fabric/protos/peer&#8220;
)
type SmartContract struct {
}
Owner string `json:&#8221;Owner&#8221;`
TotalSupply uint `json:&#8221;TotalSupply&#8221;`
TokenName string `json:&#8221;TokenName&#8221;`
TokenSymbol string `json:&#8221;TokenSymbol&#8221;`
BalanceOf map[string]uint `json:&#8221;BalanceOf&#8221;`
}
token.BalanceOf[token.Owner] = token.TotalSupply;
}
if(token.BalanceOf[_from] >= _value){
token.BalanceOf[_from] -= _value;
token.BalanceOf[_to] += _value;
}
}
return token.BalanceOf[_from]
}
if(token.BalanceOf[token.Owner] >= _value){
token.BalanceOf[token.Owner] -= _value;
token.TotalSupply -= _value;
}
}
if(token.BalanceOf[_from] >= _value){
token.BalanceOf[_from] -= _value;
token.TotalSupply -= _value;
}
}
token.TotalSupply += _value;
return shim.Success(nil)
}
Owner: &#8220;netkiller&#8221;,
TotalSupply: 10000,
TokenName: &#8220;代币通正&#8221;,
TokenSymbol: &#8220;COIN&#8221;,
BalanceOf: map[string]uint{}}
stub.PutState(&#8220;Token&#8221;, tokenAsBytes)
fmt.Println(&#8220;Added&#8221;, tokenAsBytes)
}
return shim.Error(&#8220;Incorrect number of arguments. Expecting 2&#8221;)
}
token := Token{}
token.transfer(args[1],args[2],args[3])
stub.PutState(args[0], tokenAsBytes)
}
return shim.Error(&#8220;Incorrect number of arguments. Expecting 1&#8221;)
}
token := Token{}
amount := token.balance(args[1])
}
function, args := stub.GetFunctionAndParameters()
// Route to the appropriate handler function to interact with the ledger appropriately
if function == &#8220;balanceToken&#8221; {
return s.balanceToken(stub, args)
} else if function == &#8220;initLedger&#8221; {
return s.initLedger(stub)
} else if function == &#8220;transferToken&#8221; {
return s.transferToken(stub, args)
}
}
func main() {
err := shim.Start(new(SmartContract))
if err != nil {
fmt.Printf(&#8220;Error creating new Smart Contract: %s&#8221;, err)
}
}
合约代码的测试
func main(){
token := &Token{
Owner: &#8220;netkiller&#8221;, // 代币管理者
TotalSupply: 10000, // 代币发行总量
TokenName: &#8220;积分连&#8221;, // 代币名称
TokenSymbol: &#8220;NEO&#8221;, // 代币符号 NEO
BalanceOf: map[string]uint{}}
token.initialSupply() // 初始化代币
fmt.Println(token.balance(&#8220;netkiller&#8221;)) // 查询余额
token.transfer(&#8220;netkiller&#8221;,&#8221;neo&#8221;, 100) // 转账,这里账号使用用户ID,没有使用以太坊钱包那样的哈希值,因为哈希值不便于记忆。
fmt.Println(token.balance(&#8220;netkiller&#8221;))
fmt.Println(token.balance(&#8220;neo&#8221;))
}
我们可以建立很多套这样的比,例如水果币,蔬菜币,流量币...
开发一个小型交易所难度也不大,让用户在交易所中交易这些币。