知乎專欄 | 多維度架構 | | | 微信號 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可以看到溯源數據和整個流程的視頻記錄。哇真實,可信。同時掃碼還有區塊鏈積分的激勵。
整個過程,除了大閘蟹是假的,其他環節全是真實的。最終所謂的區塊鏈溯源僅僅成為了一種營銷工具和手段。它並不能保證物品是真實性。
同理,黑龍江五常大米,福建武夷山茶葉也可以用類似方法造假。防偽技術解決了中間環節的安全問題。例如中間調包,但是源頭控制不了。
真正解決食品安全問題需要靠法制和公民的道德。這是一個禮崩樂壞,底層互害的社會,日本不需要任何區塊鏈溯源,即使是落後的泰國也不需要什麼區塊鏈的加持來保證食品安全。