Home | 簡體中文 | 繁體中文 | 雜文 | 知乎專欄 | Github | OSChina 博客 | 雲社區 | 雲棲社區 | Facebook | Linkedin | 視頻教程 | 打賞(Donations) | About
知乎專欄多維度架構 | 微信號 netkiller-ebook | QQ群:128659835 請註明“讀者”

2.2. 區塊鏈防偽溯源應用場景

食品安全溯源

下面的方案,同樣適合藥品安全溯源

2.2.1. 背景

需求是通過區塊鏈跟蹤產品,實現產品產地,生產,流通等環節溯源。

需求歸納,需要實現下面幾點:

產品具備通用的屬性,例如名稱,價格,重量,顏色,體積等等

生產銷售鏈條跟蹤

涉及環節,農產品的供應鏈是一個非常複雜的過程,涉及多方,農業局、衛生局、藥監局、工商局、環保局等多個部門交織其中。

參與者角色,我們為每個環節的參與者分配一個以太坊賬號,例如每個供應商一個賬號,每個代理商一個賬號。這樣任何一方經手後都會使用自己的賬號想合約中添加數據。

2.2.2. 如何實現

首先,要理解什麼是物品,什麼是數據。

其次,要明白物品跟數據的關係,物品怎麼跟數據建立關係。

食品是物品,物品與數據並無關聯,所謂溯源是指一連串的數據。數據是可以偽造的,我們可以偽造一連串溯源數據,然後上鏈。這些數據都是安全正常供應鏈數據偽造到。所以數據只是數據,與物品沒有任何關係。

怎樣把物品和數據建立關係呢?我們通常會借助中間載體。例如

  1. 貼二維碼,RFID標籤,NFC標籤 。或者通過激光打碼到物品上
  2. 裝箱,裝瓶,裝盒子
  3. 提取生物特徵信息

貼二維碼,RFID和NFC標籤是常規做法,將物品上貼上標籤,標籤帶有唯一標示,標籤與區塊鏈上的數據建立關聯。這樣當讀取標籤的時候,就能調出區塊鏈上的數據。

物品裝進盒子,盒子上有唯一標示,盒子與區塊鏈數據建立關聯。

生物特徵是指,例如人與數據建立連接,可以採集面部數據,眼部虹膜數據,指紋,牙齒,甚至DNA....... 然後用這些特徵與數據建立關係。食品溯源通常使用貼標籤和裝盒子的方法,不會使用生物識別技術。

仍然存在的問題:

首先說說貼標籤,這種方式並不能保證100%真實,揭開標籤,轉帖到其他商品上,理論上那個產品就是真實的。雖然有易碎紙技術,道高一尺魔高一丈,作假者有各種手段揭開標籤。RFID/NFC理論上不可造假,實際上是可以了,只是作假成本的問題,是否值得。

在說說裝箱,裝瓶防偽以茅台為代表,雖然廠家可以使用一次性瓶子,盒子,箱子,制假者還是前面說的那句道高一尺魔高一丈。它們在茅台瓶子上打孔,採用真瓶裝假酒的手法作假。

2.2.3. 安全問題

我將安全劃分為六層,分別是:

		
	+----------+-----------------------------+
	| 實體層    | 物                          |
	+----------+-----------------------------+
	| 用戶層    | 人                          |
	+----------+-----------------------------+
	| 網絡層    | 網絡                         |
	+----------+-----------------------------+
	| 應用層    | 操作系統,應用伺服器           |
	+----------+-----------------------------+
	| 業務邏輯層 | 功能,業務邏輯                |
	+----------+-----------------------------+
	| 存儲層    | 物理存儲,硬碟                |
	+----------+-----------------------------+
		
		

並不是實施了區塊鏈技術就安全無憂了,安全分為很多層,區塊鏈只能做到網絡層和存儲層的安全。區塊鏈無法解決用戶層,應用層,邏輯層等安全問題,他只能保證存儲在硬碟上的區塊不被修改。

因為區塊鏈僅僅能解決數據存儲層的安全問題,不能保證上鏈的數據是真實的,上鏈前絶對不會被篡改;所以僅僅朔源,不考慮防偽是沒有意義的,防偽仍然是重中之重。

2.2.4. 防偽問題

如何做防偽呢,這個領域很多公司已經探索多年,各種高科技應用,武裝到牙齒,但仍沒有解決假貨問題。

區塊鏈的出現很可能是一個突破,我們只需將現有成熟的防偽技術與區塊鏈結合即可。

現在流行的訪問技術太多了,我傾向于採用二維碼,RFID,NFC技術,二維碼與互聯網緊密相連。

2.2.5. 性能問題

區塊鏈目前的底層只適合做,低頻高價值的業務。

區塊鏈的讀取性能通常是沒有問題的,但是區塊鏈的寫入實際上無論你用多少個伺服器節點都不能提升,因為寫入區塊需要做共識算法,這步操作,會在所有節點上進行,同時還需要加密運算,這些操作都是 CPU 密集型操作。所以寫入操作是存在瓶頸的。

解決這個問題,我想出了幾種方案:

性能解決方案

  • 通過消息隊列技術非同步寫入,將需要寫入的區塊放入隊列,非同步完成上鏈操作。

  • 並行寫入,我們可以建設多個區塊鏈平台。多個平台同時服務于業務。

為了達到去中心化並行寫入,我們將在客戶端通過算法,匹配伺服器。而不是在兩個平台前面增加負載均衡。因為這樣又回到了中心化系統。

2.2.6. 顆粒度問題

朔源的顆粒度問題,例如“紅酒”的溯源,我們是將單位溯源做到箱呢?還是打,或是瓶呢?

我們用“四象限法則”分析

		
                       高價值
                   o     |
                         |    o
                         |
    低頻率  --------------+-------------  高頻率 操作頻率
                         |
            o            |	o	
                         |
                       低價值		
                        
                      物品價值   
		
		

通過觀察上面圖,我們可以看到可以有四種情況,低頻低價值,低頻高價值,高頻高價值,高頻低價值

我認為對於低頻高價值和高頻高價值的業務,儘量做到最小顆粒度。

而對於低頻低價值和高頻低價值的業務,可以顆粒度更粗。

2.2.7. 存儲規劃

如果是高頻低價值的業務,那麼溯源數據源源將會不斷的被添加到區塊,以此同時區塊的訪問率極低。遲早會達到一個臨界值。

所以你要規劃存儲,例如溯源數據的過期時間,對於 hyperledger 可以使用 DelState(key) 刪除歷史數據。

如果是高頻高價值的業務是否要考慮永久保留數據呢?

這些問題都是需要考慮的。因為目前我們還不知道區塊鏈的存儲臨界值。

2.2.8. 大數據問題

區塊鏈替代不了資料庫,它與資料庫是互補關係。

對於低頻的業務,通常傳統資料庫足以應付。那麼對於高頻操作的業務呢?暫時可能沒有問題,但總有一天會遇到瓶頸。

綜上所述,溯源項目資料庫規劃決不能少。同時還要考慮數據倉庫和後期數據挖掘。因為用戶使用微信或者我們的APP掃瞄二維碼,我們可以獲得很多有價值的數據。

手上沒有 Vision 使用文本簡單的繪製了一幅圖

		
                             +------------------------+
                             |    User -> QR Code     |
                             +------------------------+
                                 |              |    
                                 V              V
+---------------+    +---------------+    +---------------+
| Search Engine |<-- | Microservice  |    |  Microservice |
+---------------+    +---------------+    +---------------+
                           |                     |
         +----------------------------------+    |
         |                 |                |    |
         V                 V                V    V
    +----------+     +------------+    +-------------+ 
    | Database |     | Big Data   |    | Blockchain  |
    +----------+     +------------+    +-------------+
    | MySQL    |     | Hadoop     |    | Hyperledger |
    | NoSQL    |     | Hive/Hbase |    | Chaincode   |
    +----------+     +------------+    +-------------+	
         |   |                ^               ^
         |   +------ ETL -----|               |
         |                                    |
         +----------- Message Queue ----------o		
		
		

區塊鏈之外的很多複雜的需求我們需要借助大數據系統和搜索技術。

區塊鏈的弱點是無法做複雜的查詢,這裡我們會用到搜索引擎技術解決,實際上搜索引擎角色是給區塊鏈做索引。

上圖數據寫入時,保存了四份,分別在搜索引擎,關係型資料庫,數據倉庫和區塊的

具體怎麼實現,有很多方式,這裡就不討論了,否則就跑題了。

2.2.9. BI商業智能

數據採集,大數據分析

溯源信息的查詢是通過用戶手機終端實現,有幾種途徑,微信掃二維碼,APP掃二維碼,微信小程序等等。

掃碼的同時還可以收集用戶數據,我們可以收集到很多有價值的數據,例如地理位置,手機號碼,性別,年齡等等......

有了這些數據便可以挖掘出有價值的數據,甚至可以將數據提供給生產企業作參考。

傳統銷售數據只能跟蹤到地域,也就是統計出地域銷量,沒法監控到最後一公里的數據,而我們主要是採集商品最後一公里的數據。

我們能做到用戶消費後,呼叫中心立即跟進回訪,還能在用戶快用完商品是向用戶推送促銷信息,以及客服二次跟進。

大數據能做什麼?

  1. 用戶行為分析,用戶的喜好,這些數據能為後面精準推送提供支持。

  2. 消費與地理分析的關係

  3. 年齡段與購買力的關係

  4. 區域產品的存量,例如:用戶掃瞄了一次二維碼,可能用戶就已經使用了改產品。我們就知道該地區投放的1000件商品被消耗了意見。

  5. 性別與消費習慣

  6. 兩次間隔消費時間

  7. 活躍用戶和沉睡用戶

2.2.10. 採集終端

溯源數據怎麼錄入呢?例如我們開發一個設備,二維碼掃瞄槍,內置安卓系統。

我們不清楚他們的教育背景以及學習能力,所以設計原則是儘量傻瓜化,降低數據錄入難度和學習難度,終端開機後互動教學,走一遍流程即可上手。

首先將溯源環節的每個節點通過後台事先寫入資料庫,接下來通過GIS地理信息系統匹配。

		
		
UUID -> 二維碼 -> 設備掃瞄二維碼激活-> 入資料庫 -> 非同步消息隊列 -> 上鏈 > ---+
                       ^                                             |
                       |                                             |
                       +------------------- 追加數據 -----------------+ 
		
		

終端會幫助用戶欲錄入信息,用戶可以在信息基礎上修改或者重寫。同時終端支持圖片,圖像記錄上傳。

對於圖片還能實現 EXIF 數據保存,包括圖片描述信息,地理信息等等......

2.2.11. 多媒體數據

這裡我們需要考慮是否需要記錄多媒體數據,這裡的多媒體指圖像,聲音,甚至3D掃瞄數據等等......

對於圖片、音頻與視頻,我們可以將它整合到採集終端上,然後非同步上傳到去中心化的分散式檔案系統中。

去中心化的分散式檔案系統能實現,一張圖片一個hash值,通過hash值訪問圖片,圖片被同步到相鄰節點實現去中心化,圖片被修改hash值隨之變化數據便無效。

2.2.12. 物流介面

使用物流單好通過物流公司提供的藉口獲得物流數據,然後寫入到區塊。

2.2.13. 如何激勵用戶

防偽技術做了,區塊鏈溯源也做了,那麼對於用戶來說,他可能懶得去掃你的二維碼,怎麼辦呢?

這裡需要激勵用戶,怎樣激勵用戶,我的方案是送代幣。

首先代幣不僅能夠購買物品,還能交易,流通,形成一個小的商業閉環。其次目前代幣已經氾濫 99% 可能是空氣幣,這裡我們需要將代幣的價值與物品對價,類似金本位/銀本位。

怎樣操作呢?例如一個代幣等於一斤水果,無論代幣怎樣炒作,最終用戶不想玩下去了,就來換水果,也可以是大米,食用油等等...

關於怎樣使用代幣來做積分系統請參考我的另一篇文章 《使用代幣替代傳統積分系統》 ,你可以在搜索引擎中找到

根據業務需要,可以發行佈置一套幣,例如水果幣,流量幣,話費幣,每種幣的功能不同,這些幣可以在交易所中撮合交易,例如賣出水果幣,換成流量幣等等。

由於國家的法規問題,代幣系統設計原則一定是代幣只能用來購買商城中的物品,不能直接兌換成RMB,否則會觸碰到國家的紅線。但是通過交易所,幣幣之間兌換我們就控制不了了。

另外掃瞄二維碼顯示溯源防偽信息的同時我們有很多可以操作空間,可以獲取用戶地理位置,手機號碼等等信息,為後面大數據分析埋點。

用戶激勵手段

  1. 分享激勵

  2. 好評激勵

  3. 用戶等級激勵

  4. 代幣激勵

  5. 用戶排名,PK排行榜

  6. 成就勛章

  7. 身份標籤,黃馬甲:)

等等,手段眾多,目的是讓用戶查詢溯源信息,手機用戶數據,鼓勵代幣消費等等.......

2.2.14. 上鏈

並不是所有數據都上鏈,哪些數據上鏈呢?

產地(出生、生長)、採購、加工(檢疫、屠宰)、庫存、運輸、銷售、配送等等......

2.2.15. 以太坊解決方案

我們設計一個簡單的合約,模擬上面提到的解決方案

		
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 < attr_number);
        Attribute memory item = attribute[_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;
        }
    }

	// 獲得中轉信息
    function getLogistics(uint _number) public view returns(address, string, State, string) {
        require(_number < number);

        Logistics memory node = stations[_number];
        
		return (node.owner, node.date, node.status, node.message);
	}
	
	// 或者轉中站數量
	function getLogisticsCount() public view returns(uint){
	    return number;
	}
    
    // 客戶留言
    	function addGuestbook(address _owner, string message) public{
	    guestbook[_owner] = message;
	}
}
		
		

怎樣使用這個合約呢?合約部署,需要輸入三個參數,分別是名稱,價格和裝量

		
Trace(string _name, uint _price, uint _weight) 	
		
		

產品屬性可以在出廠前設置,一旦出廠進入物流階段就不允許在更改了。

2.2.15.1. 應用場景一

調用合約案例一,這是沒有經過深加工的原產品案例。例如 Trace("山羊肉", 25, 50)

		
var contract;
Trace.deployed().then(function(instance){contract=instance;});
contract.getName();
contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","顏色", "", "黑色")
contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","產地", "", "內蒙古")
contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","出生", "2017-01-12", "XXX牧場")
contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","宰殺", "2018-02-12", "XXX宰殺")

contract.putLogistics("0x627306090abab3a6e1400e9345bc60c78a8bef57","2018-02-20",0,"XXX牧場");
contract.putLogistics("0x627306090abab3a6e1400e9345bc60c78a8bef57","2018-02-20",1,"XXX屠宰公司");
contract.putLogistics("0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef","2018-02-22",2,"XXX檢驗檢疫");
contract.putLogistics("0xf17f52151ebef6c7334fad080c5704d77216b732","2018-02-21",3,"XXX一級經銷商");
contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-23",3,"XXX二級經銷商");
contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-24",3,"XXX批發中心");
contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-25",3,"XXX超市");
contract.putLogistics("0x0d1d4e623d10f9fba5db95830f7d3839406c6af2","2018-02-26",4,"用戶包裹收到");

contract.getNode(); // 獲得物流經過的轉運站數量		
		
			

2.2.15.2. 應用場景二

調用合約案例二,這是深加工的產品案例。例如 Trace("氂牛肉乾", 80, 500)

		
var contract;
Trace.deployed().then(function(instance){contract=instance;});
contract.getName();
contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","調和油", "2016-10-10", "銀龍魚牌")
contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","辣椒粉", "2016-10-30", "西藏XXX公司生產")
contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","生抽", "2016-01-12", "XXX生抽,XXX生產")
contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","山梨酸鉀", "2017-02-12", "XXX生產")
contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","防腐劑", "2017-02-12", "XXX生產")
contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","氂牛肉", "2017-02-12", "XXX牧場")

contract.putLogistics("0x627306090abab3a6e1400e9345bc60c78a8bef57","2018-02-20",0,"XXX牧場");
contract.putLogistics("0x627306090abab3a6e1400e9345bc60c78a8bef57","2018-02-20",1,"XXX公司生產");
contract.putLogistics("0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef","2018-02-22",2,"XXX通過QA、QC");
contract.putLogistics("0xf17f52151ebef6c7334fad080c5704d77216b732","2018-02-21",3,"XXX一級經銷商");
contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-23",3,"XXX二級經銷商");
contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-24",3,"XXX批發中心");
contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-25",3,"XXX超市");
contract.putLogistics("0x0d1d4e623d10f9fba5db95830f7d3839406c6af2","2018-02-26",4,"用戶包裹收到");

contract.getNode(); // 獲得物流經過的轉運站數量		
		
			

2.2.15.3. 用戶留言

		
contract.addGuestbook("0x0d1d423e623d10f9d10f9d10f9d10f9d10f9fba5","東西好吃,下次還買,給好評");
		
			

2.2.16. Hyperledger 解決方案

由於家裡在刷牆,伺服器收起來了,沒有開發環境,只能提供部分參考代碼,無法提供合約完整代碼,只是給大家一個思路,原理很上面以太坊的合約類似。

2.2.16.1. 溯源合約涉及

		
package main

import "fmt"
import "encoding/json"

const (
	Origin = iota 	// 0
	Factory        	// 1
	QA       		// 2
	Shipping		// 3
	Received		// 4
	Pending			// 5
	Supermarket		// 6
)

type structElement struct {
	Name string `json:"name"`
	Company string `json:"company"`
	Description string `json:"description"`
}

type structLogistics struct {
    Stations string `json:"stations"`	// 中轉站
	Date	string  `json:"date"`  // 轉運日期
	Status	uint8	`json:"status"`  // 狀態
    Message    string `json:"message"` // 留言信息
}

type Trace struct {
	Name string	`json:"name"`
	Address string	`json:"address"`
	Attribute	map[string]string 	`json:"attribute"`
	Element		[]structElement		`json:"element"`
	Logistics	map[string]structLogistics	`json:"logistics"`
}

func (trace *Trace) setName(_name string) {
    trace.Name = _name
}

func (trace *Trace) getName() string {
    return trace.Name
}

func (trace *Trace) putAttribute(_key string, _value string) {
    trace.Attribute[_key] = _value
}

func (trace *Trace) putLogistics(_key string, _value structLogistics) {
    trace.Logistics[_key] = _value
}

func main(){
	
	trace := &Trace{
		Name: "氂牛肉乾",
		Address: "內蒙古呼和浩特",
		Attribute: map[string]string{},
		Element: []structElement{structElement{Name:"塑料袋",Company: "XXX塑料製品有限公司", Description: "外包裝"},structElement{Name:"辣椒粉",Company: "XXX調味品有限公司", Description: "採摘年份2016-10-10"},structElement{Name:"調和油",Company: "XXX調味品有限公司", Description: "生產日期2016-10-10"}},
		Logistics: map[string]structLogistics{}}
	
	trace.putAttribute("Color","Red")
	trace.putAttribute("Size","10")
	trace.putAttribute("Weight","100kg")
	
	trace.putLogistics("1", structLogistics{"呼和浩特","2016-10-15", Origin, "氂牛收購"})
	trace.putLogistics("2", structLogistics{"呼和浩特","2016-10-18", Factory, "氂牛宰殺"})
	trace.putLogistics("3", structLogistics{"呼和浩特","2016-10-15", QA, "經過質檢"})
	trace.putLogistics("4", structLogistics{"北京市","2016-10-15", Shipping, "運輸中"})
	trace.putLogistics("5", structLogistics{"杭州市","2016-10-15", Shipping, "XXX冷庫"})
	trace.putLogistics("5", structLogistics{"深圳市","2016-10-15", Supermarket, "XXX超市"})
	trace.putLogistics("5", structLogistics{"龍華區","2016-10-15", Received, "用戶簽收"})
	

	traceJson, _ := json.Marshal(trace)
	fmt.Println(string(traceJson))

}
		
			
2.2.16.1.1. 食品安全朔源
				
	trace := &Trace{
		Name: "氂牛肉乾",
		Address: "內蒙古呼和浩特",
		Attribute: map[string]string{},
		Element: []structElement{structElement{Name:"塑料袋",Company: "XXX塑料製品有限公司", Description: "外包裝"},structElement{Name:"辣椒粉",Company: "XXX調味品有限公司", Description: "採摘年份2016-10-10"},structElement{Name:"調和油",Company: "XXX調味品有限公司", Description: "生產日期2016-10-10"}},
		Logistics: map[string]structLogistics{}}
	
	trace.putAttribute("Color","Red")
	trace.putAttribute("Size","10")
	trace.putAttribute("Weight","100kg")
	
	trace.putLogistics("1", structLogistics{"呼和浩特","2016-10-15", Origin, "氂牛收購"})
	trace.putLogistics("2", structLogistics{"呼和浩特","2016-10-18", Factory, "氂牛宰殺"})
	trace.putLogistics("3", structLogistics{"呼和浩特","2016-10-15", QA, "經過質檢"})
	trace.putLogistics("4", structLogistics{"北京市","2016-10-15", Shipping, "運輸中"})
	trace.putLogistics("5", structLogistics{"杭州市","2016-10-15", Shipping, "XXX冷庫"})
	trace.putLogistics("5", structLogistics{"深圳市","2016-10-15", Supermarket, "XXX超市"})
	trace.putLogistics("5", structLogistics{"龍華區","2016-10-15", Received, "用戶簽收"})				
				
				
2.2.16.1.2. 水平移植

這個方案可以水平移植到其他領域,例如 藥品安全溯源

				
	trace := &Trace{
		Name: "強身大力丸",
		Address: "深圳是XXX製藥有限公司",
		Attribute: map[string]string{},
		Element: []structElement{
			structElement{Name:"枸杞",Company: "寧夏XXX農業有限公司", Description: "採摘年份2016-10-10,10g"},
			structElement{Name:"茯苓",Company: "河南XXX農業有限公司", Description: "採摘年份2016-10-10,20kg"},
			structElement{Name:"XXX",Company: "XXX有限公司", Description: "生產日期2016-10-10"},
			structElement{Name:"XXX",Company: "XXX有限公司", Description: "生產日期2016-10-10"},
			...
			...
			structElement{Name:"塑料包裝",Company: "XXX有限公司", Description: "生產日期2016-10-10"},
			structElement{Name:"包裝盒",Company: "XXX有限公司", Description: "生產日期2016-10-10"}
			},
		Logistics: map[string]structLogistics{}}
	
	trace.putAttribute("Color","Red")
	trace.putAttribute("Size","10")
	...
	...
	trace.putAttribute("Weight","100kg")
	
	trace.putLogistics("1", structLogistics{"呼和浩特","2016-10-15", Origin, "原材料...."})
	trace.putLogistics("2", structLogistics{"呼和浩特","2016-10-18", Factory, "生產...."})
	trace.putLogistics("3", structLogistics{"呼和浩特","2016-10-15", QA, "經過質檢"})
	trace.putLogistics("3", structLogistics{"XXX市藥品監督局","2016-10-15", QA, "經過質檢"})
	trace.putLogistics("4", structLogistics{"北京市","2016-10-15", Shipping, "運輸中"})
	trace.putLogistics("5", structLogistics{"杭州市","2016-10-15", Shipping, "XXX冷庫"})
	trace.putLogistics("5", structLogistics{"深圳市","2016-10-15", Supermarket, "XXX超市"})
	trace.putLogistics("5", structLogistics{"龍華區","2016-10-15", Received, "用戶簽收"})				
				
				

合約落地,還需要做一些調整已適應實際場景。但基本思路是通的。

2.2.16.2. 積分通正(代幣)

我發現用以太坊思維,將以太坊代幣合約搬到 hyperledger 上,一樣可以實現代幣的功能,這個代幣除了不能上交易所,基本滿足我們替代積分系統的需求,下面是我寫了這樣一個合約,在超級賬本上實現類似以太坊的代幣轉賬功能。

			
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strconv"

	"github.com/hyperledger/fabric/core/chaincode/shim"
	sc "github.com/hyperledger/fabric/protos/peer"
)

// Define the Smart Contract structure
type SmartContract struct {
}

type Token struct {
	Owner			string	`json:"Owner"`
	TotalSupply 	uint	`json:"TotalSupply"`
	TokenName 		string	`json:"TokenName"`
	TokenSymbol 	string	`json:"TokenSymbol"`
	BalanceOf		map[string]uint	`json:"BalanceOf"`
}

func (token *Token) initialSupply(){
	token.BalanceOf[token.Owner] = token.TotalSupply;
}

func (token *Token) transfer (_from string, _to string, _value uint){
	if(token.BalanceOf[_from] >= _value){
		token.BalanceOf[_from] -= _value;
		token.BalanceOf[_to] += _value;
	}
}

func (token *Token) balance (_from string) uint{
	return token.BalanceOf[_from]
}

func (token *Token) burn(_value uint) {
	if(token.BalanceOf[token.Owner] >= _value){
		token.BalanceOf[token.Owner] -= _value;
		token.TotalSupply -= _value;
	}
}

func (token *Token) burnFrom(_from string, _value uint) {
	if(token.BalanceOf[_from] >= _value){
		token.BalanceOf[_from] -= _value;
		token.TotalSupply -= _value;
	}
}

func (token *Token) mint(_value uint) {
	
	token.BalanceOf[token.Owner] += _value;
	token.TotalSupply += _value;
	
}

func (s *SmartContract) Init(stub shim.ChaincodeStubInterface) sc.Response {
	return shim.Success(nil)
}

func (s *SmartContract) initLedger(stub shim.ChaincodeStubInterface) sc.Response {
	
	token := &Token{
		Owner: "netkiller",
		TotalSupply: 10000,
		TokenName: "代幣通正",
		TokenSymbol: "COIN",
		BalanceOf: map[string]uint{}}
	
	token.initialSupply()

	tokenAsBytes, _ := json.Marshal(token)
	stub.PutState("Token", tokenAsBytes)
	fmt.Println("Added", tokenAsBytes)
	
	return shim.Success(nil)
}

func (s *SmartContract) transferToken(stub shim.ChaincodeStubInterface, args []string) sc.Response {

	if len(args) != 3 {
		return shim.Error("Incorrect number of arguments. Expecting 2")
	}

	tokenAsBytes, _ := stub.GetState(args[0])
	token := Token{}

	json.Unmarshal(tokenAsBytes, &token)
	token.transfer(args[1],args[2],args[3])

	tokenAsBytes, _ = json.Marshal(token)
	stub.PutState(args[0], tokenAsBytes)

	return shim.Success(nil)
}

func (s *SmartContract) balanceToken(stub shim.ChaincodeStubInterface, args []string) sc.Response {

	if len(args) != 1 {
		return shim.Error("Incorrect number of arguments. Expecting 1")
	}

	tokenAsBytes, _ := stub.GetState(args[0])
	token := Token{}

	json.Unmarshal(tokenAsBytes, &token)
	amount := token.balance(args[1])

	return shim.Success(amount)
}

func (s *SmartContract) Invoke(stub shim.ChaincodeStubInterface) sc.Response {

	// Retrieve the requested Smart Contract function and arguments
	function, args := stub.GetFunctionAndParameters()
	// Route to the appropriate handler function to interact with the ledger appropriately
	if function == "balanceToken" {
		return s.balanceToken(stub, args)
	} else if function == "initLedger" {
		return s.initLedger(stub)
	} else if function == "transferToken" {
		return s.transferToken(stub, args)
	}

	return shim.Error("Invalid Smart Contract function name.")
}

// The main function is only relevant in unit test mode. Only included here for completeness.
func main() {

	// Create a new Smart Contract
	err := shim.Start(new(SmartContract))
	if err != nil {
		fmt.Printf("Error creating new Smart Contract: %s", err)
	}
}

			
			

合約代碼的測試

			
func main(){
	
	token := &Token{
		Owner: "netkiller",		// 代幣管理者
		TotalSupply: 10000,		// 代幣發行總量
		TokenName: "積分連",		// 代幣名稱
		TokenSymbol: "NEO",		// 代幣符號 NEO
		BalanceOf: map[string]uint{}}
	
	token.initialSupply()		// 初始化代幣
	
	fmt.Println(token.balance("netkiller"))	// 查詢餘額

	token.transfer("netkiller","neo", 100)	// 轉賬,這裡賬號使用用戶ID,沒有使用以太坊錢包那樣的哈希值,因為哈希值不便于記憶。

	fmt.Println(token.balance("netkiller"))
	fmt.Println(token.balance("neo"))

}
			
			

我們可以建立很多套這樣的比,例如水果幣,蔬菜幣,流量幣...

開發一個小型交易所難度也不大,讓用戶在交易所中交易這些幣。

2.2.17. 總結

區塊鏈技術不能徹底解決食品的防偽和溯源問題,但是他能增加造假的難度。目前還不能解決源頭造假的問題。

舉例陽澄湖大閘蟹怎麼用區塊鏈造假,首先將外地蟹運往陽澄湖洗澡。將螃蟹打撈上來,期間記錄過程,拍攝視頻,然後貼上二維碼,RFID,NFC標籤,在裝上有防偽的箱子(帶有IoT技術,開箱需要掃碼,中間運輸過程中一旦開啟時將上報公司,且箱子是一次性的,開啟後無法在還原,只能報廢),同時也給物流公司的集裝箱上了電子鎖,電子鎖有記錄GPS軌跡的功能,還有加速度感測器,被開啟或中途長時間停車都會通過 IoT 技術通知公司。武裝到牙齒了吧!!!

一切就緒後,準備防偽證書,視頻,產品信息,將這些數據上鏈。發貨,記錄供應鏈數據,物流數據,GPS行車軌跡,中轉站。最後到大商超,上架。所有數據收集完畢,更新區塊鏈,追加供應鏈數據。

現在用戶下載溯源APP或小程序,掃碼或NFC可以看到溯源數據和整個流程的視頻記錄。哇真實,可信。同時掃碼還有區塊鏈積分的激勵。

整個過程,除了大閘蟹是假的,其他環節全是真實的。最終所謂的區塊鏈溯源僅僅成為了一種營銷工具和手段。它並不能保證物品是真實性。

同理,黑龍江五常大米,福建武夷山茶葉也可以用類似方法造假。防偽技術解決了中間環節的安全問題。例如中間調包,但是源頭控制不了。

真正解決食品安全問題需要靠法制和公民的道德。這是一個禮崩樂壞,底層互害的社會,日本不需要任何區塊鏈溯源,即使是落後的泰國也不需要什麼區塊鏈的加持來保證食品安全。