本文是在閱讀《區(qū)塊鏈開發(fā)實(shí)戰(zhàn)-Hyperledger Fabric關(guān)鍵技術(shù)與案例分析》一書的同時(shí),在實(shí)踐中記錄的一些經(jīng)驗(yàn)與分享。
供應(yīng)鏈金融本質(zhì)上是一種金融服務(wù),圍繞核心企業(yè),管理上下游中小企業(yè)的資金流和物流,并把單個(gè)企業(yè)的不可控風(fēng)險(xiǎn)轉(zhuǎn)變?yōu)楣?yīng)鏈企業(yè)整體的可控風(fēng)險(xiǎn),通過獲取各類信息,將風(fēng)險(xiǎn)控制在最低的金融服務(wù)。
在整個(gè)供應(yīng)鏈運(yùn)行中,各個(gè)企業(yè)都使用賒銷作為交易的主流方式,導(dǎo)致處于供應(yīng)鏈中上游的供應(yīng)商,很難通過"傳統(tǒng)"的信貸方式獲得銀行的資金支持,而資金短缺又會(huì)直接導(dǎo)致后續(xù)環(huán)節(jié)的停滯,甚至出現(xiàn)"斷鏈"。供應(yīng)鏈金融的出現(xiàn)可以使供應(yīng)商企業(yè)通過其應(yīng)收賬款,貿(mào)易數(shù)據(jù)等信息進(jìn)行融資。
在供應(yīng)鏈運(yùn)行過程中,各類信息分散保存在各個(gè)環(huán)節(jié)中,供應(yīng)商的貨物信息存儲(chǔ)在供應(yīng)商的倉儲(chǔ)信息中,發(fā)貨信息掌握在物流公司手中,自己信息分布在銀行系統(tǒng)中,信息流由核心企業(yè)掌握,整個(gè)供應(yīng)鏈信息不透明,各個(gè)參與發(fā)都有自己的相關(guān)系統(tǒng),導(dǎo)致整個(gè)過程非常繁瑣,針對(duì)這個(gè)問題,區(qū)塊鏈的出現(xiàn)給解決這些問題到來了曙光。
系統(tǒng)設(shè)計(jì)
在應(yīng)收賬款融資場(chǎng)景中,通常設(shè)計(jì)核心企業(yè),供應(yīng)商,金融機(jī)構(gòu)這三個(gè)參與方。供應(yīng)商發(fā)起供貨交易時(shí),向超級(jí)賬本發(fā)送供貨交易,核心企業(yè)和金融機(jī)構(gòu)簽名并確認(rèn)后,才能記賬成功。
組織
我們按照如下規(guī)則為三個(gè)組織命名:
| 機(jī)構(gòu)名稱 | 組織標(biāo)識(shí)符 | 組織ID | 組織域名 |
|---|---|---|---|
| 核心企業(yè) | CoreOrg | CoreMSP | core.jianshu.com |
| 供應(yīng)商 | SupplierOrg | SupplierMSP | supplier.jianshu.com |
| 金融機(jī)構(gòu) | BankOrg | BankMSP | bank.jianshu.com |
生成證書
配置crypto-config.yaml,使用cryptogen可以生成相關(guān)證書
OrdererOrgs:
- Name: Orderer
Domain: jianshu.com
Specs:
- Hostname: orderer
PeerOrgs:
- Name: Core
Domain: core.jianshu.com
Template:
Count: 2
Users:
Count: 2
- Name: Supplier
Domain: supplier.jianshu.com
Template:
Count: 2
Users:
Count: 2
- Name: Bank
Domain: bank.jianshu.com
Template:
Count: 2
Users:
Count: 2
啟動(dòng)系統(tǒng)
規(guī)劃好系統(tǒng)之后,整個(gè)啟動(dòng)過程與之前的一樣,不過這次我們將orderer和三個(gè)peer分別部署在4臺(tái)服務(wù)器上,服務(wù)器可以在UCloud上按小時(shí)付費(fèi)臨時(shí)使用。
具體可以參見Hyperledger Fabric開發(fā)實(shí)戰(zhàn)-02簡(jiǎn)單網(wǎng)絡(luò)的整個(gè)流程。
Chaincode
Chaincode中主要是調(diào)用方法,有三個(gè):
putvalue方法,會(huì)設(shè)置資產(chǎn)的價(jià)格
getlastvalue方法用于獲取資產(chǎn)的價(jià)格
gethistory方法用于獲取資產(chǎn)的歷史
package main
import (
"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos/peer"
"fmt"
"encoding/json"
"time"
)
var asset_name = "asset_name_a"
type scfinancechaincode struct {
}
func (t *scfinancechaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
fmt.Printf(" init success")
return shim.Success([]byte("init success"))
}
func (t *scfinancechaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response{
_,args := stub.GetFunctionAndParameters()
var opttype = args[0]
var assetname = args[1]
var optcontent = args[2]
fmt.Printf("param is %s %s %s \n",opttype,assetname,optcontent)
if opttype == "putvalue"{
stub.PutState(assetname,[]byte(optcontent))
return shim.Success([]byte("success put" + optcontent))
}else if opttype == "getlastvalue"{
var kv []byte
var err error
kv,err = stub.GetState(assetname)
if(err != nil){
return shim.Error("find error!")
}
return shim.Success(kv)
}else if opttype == "gethistory"{
keysIter,err := stub.GetHistoryForKey(assetname)
if err != nil {
return shim.Error(fmt.Sprintf("GetHistoryForKey failed,Error state: %s",err))
}
defer keysIter.Close()
var keys []string
for keysIter.HasNext(){
response,iterErr := keysIter.Next()
if iterErr != nil {
return shim.Error(fmt.Sprintf("GetHistoryForKey operation failed,Error state: %s",err))
}
txid := response.TxId
txvalue := response.Value
txstate := response.IsDelete
txtimestamp := response.Timestamp
tm := time.Unix(txtimestamp.Seconds,0)
datestr := tm.Format("2006-01-02 03:04:05 PM")
fmt.Printf(" Tx info - txid: %s value: %s if delete: %t datetime: %s\n",txid,string(txvalue),txstate,datestr)
keys = append(keys,txid)
}
jsonKeys,err := json.Marshal(keys)
if err != nil {
return shim.Error(fmt.Sprintf("query operation failed,error is %s",err))
}
return shim.Success(jsonKeys)
}else{
return shim.Success([]byte("success invoke and No operation"))
}
}
func main() {
err := shim.Start(new(scfinancechaincode))
if err != nil {
fmt.Printf("Error starting Simple chaincode: %s", err)
}
}
客戶端開發(fā)
下面是發(fā)送交易的代碼,channel首先通過sendTransactionProposal發(fā)送提案,獲取響應(yīng)之后,如果所有請(qǐng)求都成功,通過sendTransaction發(fā)送交易。
var sendTransaction = function(chaincodeId,func,chaincode_args,channelname){
let tx_id = null;
return getOrgUser4Local().then((user) =>{
tx_id = client.newTransactionID();
var request = {
chaincodeId:"supplychain-chaincode",
fcn:func,
args:chaincode_args,
chainId:"cmbcchannel666",
txId:tx_id
};
return channel.sendTransactionProposal(request);
},(err) => {
console.log("error",e);
}).then((chaincodeInvokeResult) => {
var proposalResponse = chaincodeInvokeResult[0];
var proposal = chaincodeInvokeResult[1];
var header = chaincodeInvokeResult[2];
var all_good = true;
for(var i in proposalResponse){
let one_good = false;
if(proposalResponse && proposalResponse[i].response &&
proposalResponse[i].response.status === 200){
one_good = true;
console.log("transaction is good");
}else{
console.error("transaction is bad");
}
all_good = all_good & one_good;
}
if(all_good){
/*console.info(util.format(
'successfullly: status - %s,message - "%s",metadata - "%s",endorsemenet signature:"%s"',
proposalResponse[0].response.status,proposalResponse[0].response.message,
proposalResponse[0].response.payload,proposalResponse[0].response.endorsement.signature
));*/
var request = {
proposalResponses:proposalResponse,
proposal:proposal,
header:header
}
var transactionID = tx_id.getTransactionID();
return channel.sendTransaction(request);
}
},(err) => {
console.log("error",err);
}).then((sendtransresult) => {
return sendtransresult;
},(err) =>{
console.log("error",err);
});
}