我們在業務上容器的過程中遇到了如下問題:
為解決以上問題,我們在容器平臺當中引入了 openkruise。
項目地址:https://github.com/openkruise/kruise
詳細的說明可以參考這篇文章:https://yq.aliyun.com/articles/706442
從當前 github 上的文檔來看,目前 OpenKruise 支持五種改進的控制器:
UnitedDeployment 是在 StatefulSet 基礎上的更高級抽象,通過一個資源描述可以管理多個 StatefulSet 的實例組,可實現多實例組的灰度發布與滾動升級。
Broadcast Job 實際上就是以 DaemonSet 的方式在所有節點上運行一次性 JobSidercarSet 用于 Sidercar 注入及管理
而我們要使用到的正是其 Advanced StatefulSet 的特性。關于Advanced StatefulSet更詳細的描述如下:
官方的安裝文檔可以直接參考這里:
https://github.com/openkruise/kruise/tree/master/docs/tutorial
我簡單寫下安裝步驟:
wget https://github.com/openkruise/kruise/releases/download/v0.4.0/kruise-chart.tgz
tar xf kruise-chart.tgz
cd kruise
helm install openkruise ./ -n kube-system
目前openkruise已經更新到了v0.5.0的版本。也可以直接通過阿里云的應用目錄來完成其安裝。
下面說一下更詳細的安裝過程:
1、獲取helm包
helm repo add incubator http://aliacs-k8s-cn-beijing.oss-cn-beijing.aliyuncs.com/app/charts-incubator/
helm search repo ack-kruise
helm fetch incubator/ack-kruise
tar xf ack-kruise-0.5.0.tgz
cd ack-kruise
修改values.yml文件如下:
# Default values for kruise.
revisionHistoryLimit: 3
manager:
# settings for log print
log:
# log level for kruise-manager
level: "4"
# image settings
image:
# repository for kruise-manager image
repository: hub.example.com/library/kruise-manager
# tag for kruise-manager image
tag: v0.5.0
# resources of kruise-manager container
resources:
limits:
cpu: 500m
memory: 1Gi
requests:
cpu: 500m
memory: 1Gi
metrics:
addr: localhost
port: 8080
custom_resource_enable: StatefulSet
其實這里就改了兩個東西:
然后執行安裝操作:
helm install ack-kruise -n kube-system ./
安裝完后,會生成以下五種crd:
# kubectl get crds |grep kruise
broadcastjobs.apps.kruise.io 2020-04-26T10:29:28Z
clonesets.apps.kruise.io 2020-04-26T10:29:28Z
sidecarsets.apps.kruise.io 2020-04-26T10:29:28Z
statefulsets.apps.kruise.io 2020-04-26T10:29:28Z
uniteddeployments.apps.kruise.io 2020-04-26T10:29:28Z
同時會創建一個 kruise-system 的命名空間,并在里面生成一個 pod:
# kubectl get pods -n kruise-system
NAME READY STATUS RESTARTS AGE
kruise-controller-manager-0 1/1 Running 0 55m
驗證 statefulset 資源的 webhook 是否被正常創建:
# kubectl get mutatingwebhookconfiguration -o yaml
apiVersion: v1
items:
- apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
creationTimestamp: "2020-04-26T10:29:28Z"
generation: 3
name: kruise-mutating-webhook-configuration
resourceVersion: "622944921"
selfLink: /apis/admissionregistration.k8s.io/v1/mutatingwebhookconfigurations/kruise-mutating-webhook-configuration
uid: 303a7b7f-3a62-49d7-8ef6-082ea288eeb2
webhooks:
- admissionReviewVersions:
- v1beta1
clientConfig:
caBundle: xxxxx
service:
name: kruise-webhook-server-service
namespace: kruise-system
path: /mutating-create-update-statefulset
port: 443
failurePolicy: Fail
matchPolicy: Exact
name: mutating-create-update-statefulset.kruise.io
namespaceSelector:
matchExpressions:
- key: control-plane
operator: DoesNotExist
objectSelector: {}
reinvocationPolicy: Never
rules:
- apiGroups:
- apps.kruise.io
apiVersions:
- v1alpha1
operations:
- CREATE
- UPDATE
resources:
- statefulsets
scope: '*'
sideEffects: Unknown
timeoutSeconds: 30
......
也是確保其他未用到的相關 mutatingwebhook 是關閉的。。在實際測試中,SidecarSet 資源的 mutatingwebhook 可能會導致創建的 pod 出不來。
這些webhook本質上都是kubernetes的admissioncontrol,只要你安裝了,哪怕沒有使用,當你在執行相關操作時,都需要被所有的adminssioncontrol檢測,如果admissioncontrol本身出了問題,就會導致請求無法響應的狀態。同時這些webhook類型的adminssioncontrol也會拖慢響應速度。
下面是官方提供的一個基于 openkruise 提供的 statefulset 資源的部署文件示例:
apiVersion: apps.kruise.io/v1alpha1
kind: StatefulSet
metadata:
name: demo-v1-guestbook-kruise
labels:
app.kubernetes.io/name: guestbook-kruise
app.kubernetes.io/instance: demo-v1
spec:
replicas: 3
serviceName: demo-v1-guestbook-kruise
selector:
matchLabels:
app.kubernetes.io/name: guestbook-kruise
app.kubernetes.io/instance: demo-v1
template:
metadata:
labels:
app.kubernetes.io/name: guestbook-kruise
app.kubernetes.io/instance: demo-v1
spec:
readinessGates:
# A new condition that ensures the pod remains at NotReady state while the in-place update is happening
- conditionType: InPlaceUpdateReady
containers:
- name: guestbook-kruise
image: openkruise/guestbook:v1
imagePullPolicy: Always
ports:
- name: http-server
containerPort: 3000
podManagementPolicy: Parallel # allow parallel updates, works together with maxUnavailable
updateStrategy:
type: RollingUpdate
rollingUpdate:
# Do in-place update if possible, currently only image update is supported for in-place update
podUpdatePolicy: InPlaceIfPossible
# Allow parallel updates with max number of unavailable instances equals to 2
maxUnavailable: 3
執行部署之后,啟動 pod 示例如下:
# kubectl get pods |grep demo-v1
demo-v1-guestbook-kruise-0 1/1 Running 0 62s
demo-v1-guestbook-kruise-1 1/1 Running 0 62s
demo-v1-guestbook-kruise-2 1/1 Running 0 62s
也可通過如下操作查看資源狀態:
# kubectl get sts.apps.kruise.io
NAME DESIRED CURRENT UPDATED READY AGE
demo-v1-guestbook-kruise 3 3 3 3 56s
openkruise提供的statefulset的資源名為sts.apps.kruise.io
更詳細的用法可參考:
來源:https://www.cnblogs.com/breezey/p/12925103.html
篇挺好的文章,可以細品下
內容摘要
這一部關于區塊鏈開發及運維的電子書。
為什么會寫區塊鏈電子書?因為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 |<-- | Microservice | | Microservice |+---------------+ +---------------+ +---------------+| |+----------------------------------+ || | | |V V V V+----------+ +------------+ +-------------+ | Database | | Big Data | | Blockchain |+----------+ +------------+ +-------------+| MySQL | | Hadoop | | Hyperledger || NoSQL | | Hive/Hbase | | Chaincode |+----------+ +------------+ +-------------+ | | ^ ^| +------ ETL -----| || |+----------- Message Queue ----------o
區塊鏈之外的很多復雜的需求我們需要借助大數據系統和搜索技術。
區塊鏈的弱點是無法做復雜的查詢,這里我們會用到搜索引擎技術解決,實際上搜索引擎角色是給區塊鏈做索引。
上圖數據寫入時,保存了四份,分別在搜索引擎,關系型數據庫,數據倉庫和區塊的
具體怎么實現,有很多方式,這里就不討論了,否則就跑題了。
33.2.8. BI商業智能
數據采集,大數據分析
溯源信息的查詢是通過用戶手機終端實現,有幾種途徑,微信掃二維碼,APP掃二維碼,微信小程序等等。
我們可以收集到很多有價值的數據,例如地理位置,手機號碼,性別,年齡等等......
有了這些數據便可以挖掘出有價值的數據,甚至可以將數據提供給生產企業作參考。
大數據能做什么?
33.2.9. 采集終端
溯源數據怎么錄入呢?例如我們開發一個設備,二維碼掃描槍,內置安卓系統。
我們不清楚他們的教育背景以及學習能力,所以設計原則是盡量傻瓜化,降低數據錄入難度和學習難度,終端開機后互動教學,走一遍流程即可上手。
首先將溯源環節的每個節點通過后臺事先寫入數據庫,接下來通過GIS地理信息系統匹配。
UUID -> 二維碼 -> 設備掃描二維碼激活-> 入數據庫 -> 異步消息隊列 -> 上鏈 > ---+^ || |+------------------- 追加數據 -----------------+
終端會幫助用戶欲錄入信息,用戶可以在信息基礎上修改或者重寫。同時終端支持圖片,圖像記錄上傳。
對于圖片還能實現 EXIF 數據保存,包括圖片描述信息,地理信息等等......
33.2.10. 多媒體數據
這里我們需要考慮是否需要記錄多媒體數據,這里的多媒體指圖像,聲音,甚至3D掃描數據等等......
對于圖片、音頻與視頻,我們可以將它集成到采集終端上,然后異步上傳到去中心化的分布式文件系統中。
去中心化的分布式文件系統能實現,一張圖片一個hash值,通過hash值訪問圖片,圖片被同步到相鄰節點實現去中心化,圖片被修改hash值隨之變化數據便無效。
33.2.11. 物流接口
使用物流單好通過物流公司提供的借口獲得物流數據,然后寫入到區塊。
33.2.12. 如何激勵用戶
防偽技術做了,區塊鏈溯源也做了,那么對于用戶來說,他可能懶得去掃你的二維碼,怎么辦呢?
這里需要激勵用戶,怎樣激勵用戶,我的方案是送代幣。
首先代幣不僅能夠購買物品,還能交易,流通,形成一個小的商業閉環。其次目前代幣已經泛濫 99% 可能是空氣幣,這里我們需要將代幣的價值與物品對價,類似金本位/銀本位。
怎樣操作呢?例如一個代幣等于一斤水果,無論代幣怎樣炒作,最終用戶不想玩下去了,就來換水果,也可以是大米,食用油等等...
關于怎樣使用代幣來做積分系統請參考我的另一篇文章 《使用代幣替代傳統積分系統》 ,你可以在搜索引擎中找到
根據業務需要,可以發行布置一套幣,例如水果幣,流量幣,話費幣,每種幣的功能不同,這些幣可以在交易所中撮合交易,例如賣出水果幣,換成流量幣等等。
由于國家的法規問題,代幣系統設計原則一定是代幣只能用來購買商城中的物品,不能直接兌換成RMB,否則會觸碰到國家的紅線。但是通過交易所,幣幣之間兌換我們就控制不了了。
另外掃描二維碼顯示溯源防偽信息的同時我們有很多可以操作空間,可以獲取用戶地理位置,手機號碼等等信息,為后面大數據分析埋點。
用戶激勵手段
等等,手段眾多,目的是讓用戶查詢溯源信息,手機用戶數據,鼓勵代幣消費等等.......
33.2.13. 上鏈
并不是所有數據都上鏈,哪些數據上鏈呢?
產地(出生、生長)、采購、加工(檢疫、屠宰)、庫存、運輸、銷售、配送等等......
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 < 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)
產品屬性可以在出廠前設置,一旦出廠進入物流階段就不允許在更改了。
33.2.14.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(); // 獲得物流經過的轉運站數量
33.2.14.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(); // 獲得物流經過的轉運站數量
33.2.14.3. 用戶留言
contract.addGuestbook("0x0d1d423e623d10f9d10f9d10f9d10f9d10f9fba5","東西好吃,下次還買,給好評");
33.2.15. Hyperledger 解決方案
由于家里在刷墻,服務器收起來了,沒有開發環境,只能提供部分參考代碼,無法提供合約完整代碼,只是給大家一個思路,原理很上面以太坊的合約類似。
33.2.15.1. 溯源合約涉及
package mainimport "fmt"import "encoding/json"const (Origin=iota // 0Factory // 1QA // 2Shipping // 3Received // 4Pending // 5Supermarket // 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))}
33.2.15.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, "用戶簽收"})
33.2.15.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, "用戶簽收"})
合約落地,還需要做一些調整已適應實際場景。但基本思路是通的。
33.2.15.2. 積分通正(代幣)
我發現用以太坊思維,將以太坊代幣合約搬到 hyperledger 上,一樣可以實現代幣的功能,這個代幣除了不能上交易所,基本滿足我們替代積分系統的需求,下面是我寫了這樣一個合約,在超級賬本上實現類似以太坊的代幣轉賬功能。
package mainimport ("bytes""encoding/json""fmt""strconv""http://github.com/hyperledger/fabric/core/chaincode/shim"sc "http://github.com/hyperledger/fabric/protos/peer")// Define the Smart Contract structuretype 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 argumentsfunction, args :=stub.GetFunctionAndParameters()// Route to the appropriate handler function to interact with the ledger appropriatelyif 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 Contracterr :=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", // 代幣符號 NEOBalanceOf: 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"))}
我們可以建立很多套這樣的比,例如水果幣,蔬菜幣,流量幣...
開發一個小型交易所難度也不大,讓用戶在交易所中交易這些幣。
篇挺好的文章,可以細品下
內容摘要
這一部關于區塊鏈開發及運維的電子書。
為什么會寫區塊鏈電子書?因為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 |<-- | Microservice | | Microservice |+---------------+ +---------------+ +---------------+| |+----------------------------------+ || | | |V V V V+----------+ +------------+ +-------------+ | Database | | Big Data | | Blockchain |+----------+ +------------+ +-------------+| MySQL | | Hadoop | | Hyperledger || NoSQL | | Hive/Hbase | | Chaincode |+----------+ +------------+ +-------------+ | | ^ ^| +------ ETL -----| || |+----------- Message Queue ----------o
區塊鏈之外的很多復雜的需求我們需要借助大數據系統和搜索技術。
區塊鏈的弱點是無法做復雜的查詢,這里我們會用到搜索引擎技術解決,實際上搜索引擎角色是給區塊鏈做索引。
上圖數據寫入時,保存了四份,分別在搜索引擎,關系型數據庫,數據倉庫和區塊的
具體怎么實現,有很多方式,這里就不討論了,否則就跑題了。
33.2.8. BI商業智能
數據采集,大數據分析
溯源信息的查詢是通過用戶手機終端實現,有幾種途徑,微信掃二維碼,APP掃二維碼,微信小程序等等。
我們可以收集到很多有價值的數據,例如地理位置,手機號碼,性別,年齡等等......
有了這些數據便可以挖掘出有價值的數據,甚至可以將數據提供給生產企業作參考。
大數據能做什么?
33.2.9. 采集終端
溯源數據怎么錄入呢?例如我們開發一個設備,二維碼掃描槍,內置安卓系統。
我們不清楚他們的教育背景以及學習能力,所以設計原則是盡量傻瓜化,降低數據錄入難度和學習難度,終端開機后互動教學,走一遍流程即可上手。
首先將溯源環節的每個節點通過后臺事先寫入數據庫,接下來通過GIS地理信息系統匹配。
UUID -> 二維碼 -> 設備掃描二維碼激活-> 入數據庫 -> 異步消息隊列 -> 上鏈 > ---+^ || |+------------------- 追加數據 -----------------+
終端會幫助用戶欲錄入信息,用戶可以在信息基礎上修改或者重寫。同時終端支持圖片,圖像記錄上傳。
對于圖片還能實現 EXIF 數據保存,包括圖片描述信息,地理信息等等......
33.2.10. 多媒體數據
這里我們需要考慮是否需要記錄多媒體數據,這里的多媒體指圖像,聲音,甚至3D掃描數據等等......
對于圖片、音頻與視頻,我們可以將它集成到采集終端上,然后異步上傳到去中心化的分布式文件系統中。
去中心化的分布式文件系統能實現,一張圖片一個hash值,通過hash值訪問圖片,圖片被同步到相鄰節點實現去中心化,圖片被修改hash值隨之變化數據便無效。
33.2.11. 物流接口
使用物流單好通過物流公司提供的借口獲得物流數據,然后寫入到區塊。
33.2.12. 如何激勵用戶
防偽技術做了,區塊鏈溯源也做了,那么對于用戶來說,他可能懶得去掃你的二維碼,怎么辦呢?
這里需要激勵用戶,怎樣激勵用戶,我的方案是送代幣。
首先代幣不僅能夠購買物品,還能交易,流通,形成一個小的商業閉環。其次目前代幣已經泛濫 99% 可能是空氣幣,這里我們需要將代幣的價值與物品對價,類似金本位/銀本位。
怎樣操作呢?例如一個代幣等于一斤水果,無論代幣怎樣炒作,最終用戶不想玩下去了,就來換水果,也可以是大米,食用油等等...
關于怎樣使用代幣來做積分系統請參考我的另一篇文章 《使用代幣替代傳統積分系統》 ,你可以在搜索引擎中找到
根據業務需要,可以發行布置一套幣,例如水果幣,流量幣,話費幣,每種幣的功能不同,這些幣可以在交易所中撮合交易,例如賣出水果幣,換成流量幣等等。
由于國家的法規問題,代幣系統設計原則一定是代幣只能用來購買商城中的物品,不能直接兌換成RMB,否則會觸碰到國家的紅線。但是通過交易所,幣幣之間兌換我們就控制不了了。
另外掃描二維碼顯示溯源防偽信息的同時我們有很多可以操作空間,可以獲取用戶地理位置,手機號碼等等信息,為后面大數據分析埋點。
用戶激勵手段
等等,手段眾多,目的是讓用戶查詢溯源信息,手機用戶數據,鼓勵代幣消費等等.......
33.2.13. 上鏈
并不是所有數據都上鏈,哪些數據上鏈呢?
產地(出生、生長)、采購、加工(檢疫、屠宰)、庫存、運輸、銷售、配送等等......
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 < 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)
產品屬性可以在出廠前設置,一旦出廠進入物流階段就不允許在更改了。
33.2.14.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(); // 獲得物流經過的轉運站數量
33.2.14.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(); // 獲得物流經過的轉運站數量
33.2.14.3. 用戶留言
contract.addGuestbook("0x0d1d423e623d10f9d10f9d10f9d10f9d10f9fba5","東西好吃,下次還買,給好評");
33.2.15. Hyperledger 解決方案
由于家里在刷墻,服務器收起來了,沒有開發環境,只能提供部分參考代碼,無法提供合約完整代碼,只是給大家一個思路,原理很上面以太坊的合約類似。
33.2.15.1. 溯源合約涉及
package mainimport "fmt"import "encoding/json"const (Origin=iota // 0Factory // 1QA // 2Shipping // 3Received // 4Pending // 5Supermarket // 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))}
33.2.15.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, "用戶簽收"})
33.2.15.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, "用戶簽收"})
合約落地,還需要做一些調整已適應實際場景。但基本思路是通的。
33.2.15.2. 積分通正(代幣)
我發現用以太坊思維,將以太坊代幣合約搬到 hyperledger 上,一樣可以實現代幣的功能,這個代幣除了不能上交易所,基本滿足我們替代積分系統的需求,下面是我寫了這樣一個合約,在超級賬本上實現類似以太坊的代幣轉賬功能。
package mainimport ("bytes""encoding/json""fmt""strconv""http://github.com/hyperledger/fabric/core/chaincode/shim"sc "http://github.com/hyperledger/fabric/protos/peer")// Define the Smart Contract structuretype 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 argumentsfunction, args :=stub.GetFunctionAndParameters()// Route to the appropriate handler function to interact with the ledger appropriatelyif 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 Contracterr :=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", // 代幣符號 NEOBalanceOf: 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"))}
我們可以建立很多套這樣的比,例如水果幣,蔬菜幣,流量幣...
開發一個小型交易所難度也不大,讓用戶在交易所中交易這些幣。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。