| 知乎專欄 | 多維度架構 | | | 微信號 netkiller-ebook | | | QQ群:128659835 請註明“讀者” |
下面的方案,同樣適合藥品安全溯源
需求是通過區塊鏈跟蹤產品,實現產品產地,生產,流通等環節溯源。
需求歸納,需要實現下面幾點:
產品具備通用的屬性,例如名稱,價格,重量,顏色,體積等等
生產銷售鏈條跟蹤
涉及環節,農產品的供應鏈是一個非常複雜的過程,涉及多方,農業局、衛生局、藥監局、工商局、環保局等多個部門交織其中。
參與者角色,我們為每個環節的參與者分配一個以太坊賬號,例如每個供應商一個賬號,每個代理商一個賬號。這樣任何一方經手後都會使用自己的賬號想合約中添加數據。
首先,要理解什麼是物品,什麼是數據。
其次,要明白物品跟數據的關係,物品怎麼跟數據建立關係。
食品是物品,物品與數據並無關聯,所謂溯源是指一連串的數據。數據是可以偽造的,我們可以偽造一連串溯源數據,然後上鏈。這些數據都是安全正常供應鏈數據偽造到。所以數據只是數據,與物品沒有任何關係。
怎樣把物品和數據建立關係呢?我們通常會借助中間載體。例如
貼二維碼,RFID和NFC標籤是常規做法,將物品上貼上標籤,標籤帶有唯一標示,標籤與區塊鏈上的數據建立關聯。這樣當讀取標籤的時候,就能調出區塊鏈上的數據。
物品裝進盒子,盒子上有唯一標示,盒子與區塊鏈數據建立關聯。
生物特徵是指,例如人與數據建立連接,可以採集面部數據,眼部虹膜數據,指紋,牙齒,甚至DNA....... 然後用這些特徵與數據建立關係。食品溯源通常使用貼標籤和裝盒子的方法,不會使用生物識別技術。
仍然存在的問題:
首先說說貼標籤,這種方式並不能保證100%真實,揭開標籤,轉帖到其他商品上,理論上那個產品就是真實的。雖然有易碎紙技術,道高一尺魔高一丈,作假者有各種手段揭開標籤。RFID/NFC理論上不可造假,實際上是可以了,只是作假成本的問題,是否值得。
在說說裝箱,裝瓶防偽以茅台為代表,雖然廠家可以使用一次性瓶子,盒子,箱子,制假者還是前面說的那句道高一尺魔高一丈。它們在茅台瓶子上打孔,採用真瓶裝假酒的手法作假。
我將安全劃分為六層,分別是:
+----------+-----------------------------+ | 實體層 | 物 | +----------+-----------------------------+ | 用戶層 | 人 | +----------+-----------------------------+ | 網絡層 | 網絡 | +----------+-----------------------------+ | 應用層 | 操作系統,應用伺服器 | +----------+-----------------------------+ | 業務邏輯層 | 功能,業務邏輯 | +----------+-----------------------------+ | 存儲層 | 物理存儲,硬碟 | +----------+-----------------------------+
並不是實施了區塊鏈技術就安全無憂了,安全分為很多層,區塊鏈只能做到網絡層和存儲層的安全。區塊鏈無法解決用戶層,應用層,邏輯層等安全問題,他只能保證存儲在硬碟上的區塊不被修改。
因為區塊鏈僅僅能解決數據存儲層的安全問題,不能保證上鏈的數據是真實的,上鏈前絶對不會被篡改;所以僅僅朔源,不考慮防偽是沒有意義的,防偽仍然是重中之重。
如何做防偽呢,這個領域很多公司已經探索多年,各種高科技應用,武裝到牙齒,但仍沒有解決假貨問題。
區塊鏈的出現很可能是一個突破,我們只需將現有成熟的防偽技術與區塊鏈結合即可。
現在流行的訪問技術太多了,我傾向于採用二維碼,RFID,NFC技術,二維碼與互聯網緊密相連。
區塊鏈目前的底層只適合做,低頻高價值的業務。
區塊鏈的讀取性能通常是沒有問題的,但是區塊鏈的寫入實際上無論你用多少個伺服器節點都不能提升,因為寫入區塊需要做共識算法,這步操作,會在所有節點上進行,同時還需要加密運算,這些操作都是 CPU 密集型操作。所以寫入操作是存在瓶頸的。
解決這個問題,我想出了幾種方案:
性能解決方案
通過消息隊列技術非同步寫入,將需要寫入的區塊放入隊列,非同步完成上鏈操作。
並行寫入,我們可以建設多個區塊鏈平台。多個平台同時服務于業務。
為了達到去中心化並行寫入,我們將在客戶端通過算法,匹配伺服器。而不是在兩個平台前面增加負載均衡。因為這樣又回到了中心化系統。
朔源的顆粒度問題,例如“紅酒”的溯源,我們是將單位溯源做到箱呢?還是打,或是瓶呢?
我們用“四象限法則”分析
高價值
o |
| o
|
低頻率 --------------+------------- 高頻率 操作頻率
|
o | o
|
低價值
物品價值
通過觀察上面圖,我們可以看到可以有四種情況,低頻低價值,低頻高價值,高頻高價值,高頻低價值
我認為對於低頻高價值和高頻高價值的業務,儘量做到最小顆粒度。
而對於低頻低價值和高頻低價值的業務,可以顆粒度更粗。
如果是高頻低價值的業務,那麼溯源數據源源將會不斷的被添加到區塊,以此同時區塊的訪問率極低。遲早會達到一個臨界值。
所以你要規劃存儲,例如溯源數據的過期時間,對於 hyperledger 可以使用 DelState(key) 刪除歷史數據。
如果是高頻高價值的業務是否要考慮永久保留數據呢?
這些問題都是需要考慮的。因為目前我們還不知道區塊鏈的存儲臨界值。
區塊鏈替代不了資料庫,它與資料庫是互補關係。
對於低頻的業務,通常傳統資料庫足以應付。那麼對於高頻操作的業務呢?暫時可能沒有問題,但總有一天會遇到瓶頸。
綜上所述,溯源項目資料庫規劃決不能少。同時還要考慮數據倉庫和後期數據挖掘。因為用戶使用微信或者我們的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
區塊鏈之外的很多複雜的需求我們需要借助大數據系統和搜索技術。
區塊鏈的弱點是無法做複雜的查詢,這裡我們會用到搜索引擎技術解決,實際上搜索引擎角色是給區塊鏈做索引。
上圖數據寫入時,保存了四份,分別在搜索引擎,關係型資料庫,數據倉庫和區塊的
具體怎麼實現,有很多方式,這裡就不討論了,否則就跑題了。
數據採集,大數據分析
溯源信息的查詢是通過用戶手機終端實現,有幾種途徑,微信掃二維碼,APP掃二維碼,微信小程序等等。
掃碼的同時還可以收集用戶數據,我們可以收集到很多有價值的數據,例如地理位置,手機號碼,性別,年齡等等......
有了這些數據便可以挖掘出有價值的數據,甚至可以將數據提供給生產企業作參考。
傳統銷售數據只能跟蹤到地域,也就是統計出地域銷量,沒法監控到最後一公里的數據,而我們主要是採集商品最後一公里的數據。
我們能做到用戶消費後,呼叫中心立即跟進回訪,還能在用戶快用完商品是向用戶推送促銷信息,以及客服二次跟進。
大數據能做什麼?
用戶行為分析,用戶的喜好,這些數據能為後面精準推送提供支持。
消費與地理分析的關係
年齡段與購買力的關係
區域產品的存量,例如:用戶掃瞄了一次二維碼,可能用戶就已經使用了改產品。我們就知道該地區投放的1000件商品被消耗了意見。
性別與消費習慣
兩次間隔消費時間
活躍用戶和沉睡用戶
溯源數據怎麼錄入呢?例如我們開發一個設備,二維碼掃瞄槍,內置安卓系統。
我們不清楚他們的教育背景以及學習能力,所以設計原則是儘量傻瓜化,降低數據錄入難度和學習難度,終端開機後互動教學,走一遍流程即可上手。
首先將溯源環節的每個節點通過後台事先寫入資料庫,接下來通過GIS地理信息系統匹配。
UUID -> 二維碼 -> 設備掃瞄二維碼激活-> 入資料庫 -> 非同步消息隊列 -> 上鏈 > ---+
^ |
| |
+------------------- 追加數據 -----------------+
終端會幫助用戶欲錄入信息,用戶可以在信息基礎上修改或者重寫。同時終端支持圖片,圖像記錄上傳。
對於圖片還能實現 EXIF 數據保存,包括圖片描述信息,地理信息等等......
這裡我們需要考慮是否需要記錄多媒體數據,這裡的多媒體指圖像,聲音,甚至3D掃瞄數據等等......
對於圖片、音頻與視頻,我們可以將它整合到採集終端上,然後非同步上傳到去中心化的分散式檔案系統中。
去中心化的分散式檔案系統能實現,一張圖片一個hash值,通過hash值訪問圖片,圖片被同步到相鄰節點實現去中心化,圖片被修改hash值隨之變化數據便無效。
防偽技術做了,區塊鏈溯源也做了,那麼對於用戶來說,他可能懶得去掃你的二維碼,怎麼辦呢?
這裡需要激勵用戶,怎樣激勵用戶,我的方案是送代幣。
首先代幣不僅能夠購買物品,還能交易,流通,形成一個小的商業閉環。其次目前代幣已經氾濫 99% 可能是空氣幣,這裡我們需要將代幣的價值與物品對價,類似金本位/銀本位。
怎樣操作呢?例如一個代幣等於一斤水果,無論代幣怎樣炒作,最終用戶不想玩下去了,就來換水果,也可以是大米,食用油等等...
關於怎樣使用代幣來做積分系統請參考我的另一篇文章 《使用代幣替代傳統積分系統》 ,你可以在搜索引擎中找到
根據業務需要,可以發行佈置一套幣,例如水果幣,流量幣,話費幣,每種幣的功能不同,這些幣可以在交易所中撮合交易,例如賣出水果幣,換成流量幣等等。
由於國家的法規問題,代幣系統設計原則一定是代幣只能用來購買商城中的物品,不能直接兌換成RMB,否則會觸碰到國家的紅線。但是通過交易所,幣幣之間兌換我們就控制不了了。
另外掃瞄二維碼顯示溯源防偽信息的同時我們有很多可以操作空間,可以獲取用戶地理位置,手機號碼等等信息,為後面大數據分析埋點。
用戶激勵手段
分享激勵
好評激勵
用戶等級激勵
代幣激勵
用戶排名,PK排行榜
成就勛章
身份標籤,黃馬甲:)
等等,手段眾多,目的是讓用戶查詢溯源信息,手機用戶數據,鼓勵代幣消費等等.......
我們設計一個簡單的合約,模擬上面提到的解決方案
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)
產品屬性可以在出廠前設置,一旦出廠進入物流階段就不允許在更改了。
調用合約案例一,這是沒有經過深加工的原產品案例。例如 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(); // 獲得物流經過的轉運站數量
調用合約案例二,這是深加工的產品案例。例如 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(); // 獲得物流經過的轉運站數量
由於家裡在刷牆,伺服器收起來了,沒有開發環境,只能提供部分參考代碼,無法提供合約完整代碼,只是給大家一個思路,原理很上面以太坊的合約類似。
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))
}
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, "用戶簽收"})
這個方案可以水平移植到其他領域,例如 藥品安全溯源
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, "用戶簽收"})
合約落地,還需要做一些調整已適應實際場景。但基本思路是通的。
我發現用以太坊思維,將以太坊代幣合約搬到 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"))
}
我們可以建立很多套這樣的比,例如水果幣,蔬菜幣,流量幣...
開發一個小型交易所難度也不大,讓用戶在交易所中交易這些幣。
區塊鏈技術不能徹底解決食品的防偽和溯源問題,但是他能增加造假的難度。目前還不能解決源頭造假的問題。
舉例陽澄湖大閘蟹怎麼用區塊鏈造假,首先將外地蟹運往陽澄湖洗澡。將螃蟹打撈上來,期間記錄過程,拍攝視頻,然後貼上二維碼,RFID,NFC標籤,在裝上有防偽的箱子(帶有IoT技術,開箱需要掃碼,中間運輸過程中一旦開啟時將上報公司,且箱子是一次性的,開啟後無法在還原,只能報廢),同時也給物流公司的集裝箱上了電子鎖,電子鎖有記錄GPS軌跡的功能,還有加速度感測器,被開啟或中途長時間停車都會通過 IoT 技術通知公司。武裝到牙齒了吧!!!
一切就緒後,準備防偽證書,視頻,產品信息,將這些數據上鏈。發貨,記錄供應鏈數據,物流數據,GPS行車軌跡,中轉站。最後到大商超,上架。所有數據收集完畢,更新區塊鏈,追加供應鏈數據。
現在用戶下載溯源APP或小程序,掃碼或NFC可以看到溯源數據和整個流程的視頻記錄。哇真實,可信。同時掃碼還有區塊鏈積分的激勵。
整個過程,除了大閘蟹是假的,其他環節全是真實的。最終所謂的區塊鏈溯源僅僅成為了一種營銷工具和手段。它並不能保證物品是真實性。
同理,黑龍江五常大米,福建武夷山茶葉也可以用類似方法造假。防偽技術解決了中間環節的安全問題。例如中間調包,但是源頭控制不了。
真正解決食品安全問題需要靠法制和公民的道德。這是一個禮崩樂壞,底層互害的社會,日本不需要任何區塊鏈溯源,即使是落後的泰國也不需要什麼區塊鏈的加持來保證食品安全。