Hyperledger Fabric开发实战-08 供应链金融实
本文是在阅读《区块链开发实战-Hyperledger Fabric关键技术与案例分析》一书的同时,在实践中记录的一些经验与分享。
供应链金融本质上是一种金融服务,围绕核心企业,管理上下游中小企业的资金流和物流,并把单个企业的不可控风险转变为供应链企业整体的可控风险,通过获取各类信息,将风险控制在最低的金融服务。
在整个供应链运行中,各个企业都使用赊销作为交易的主流方式,导致处于供应链中上游的供应商,很难通过"传统"的信贷方式获得银行的资金支持,而资金短缺又会直接导致后续环节的停滞,甚至出现"断链"。供应链金融的出现可以使供应商企业通过其应收账款,贸易数据等信息进行融资。
在供应链运行过程中,各类信息分散保存在各个环节中,供应商的货物信息存储在供应商的仓储信息中,发货信息掌握在物流公司手中,自己信息分布在银行系统中,信息流由核心企业掌握,整个供应链信息不透明,各个参与发都有自己的相关系统,导致整个过程非常繁琐,针对这个问题,区块链的出现给解决这些问题到来了曙光。
系统设计
在应收账款融资场景中,通常设计核心企业,供应商,金融机构这三个参与方。供应商发起供货交易时,向超级账本发送供货交易,核心企业和金融机构签名并确认后,才能记账成功。
组织
我们按照如下规则为三个组织命名:
机构名称 | 组织标识符 | 组织ID | 组织域名 |
---|---|---|---|
核心企业 | CoreOrg | CoreMSP | core.jianshu.com |
供应商 | SupplierOrg | SupplierMSP | supplier.jianshu.com |
金融机构 | BankOrg | BankMSP | bank.jianshu.com |
生成证书
配置crypto-config.yaml
,使用cryptogen可以生成相关证书
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
启动系统
规划好系统之后,整个启动过程与之前的一样,不过这次我们将orderer和三个peer分别部署在4台服务器上,服务器可以在UCloud上按小时付费临时使用。
具体可以参见Hyperledger Fabric开发实战-02简单网络的整个流程。
Chaincode
Chaincode中主要是调用方法,有三个:
putvalue方法,会设置资产的价格
getlastvalue方法用于获取资产的价格
gethistory方法用于获取资产的历史
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)
}
}
客户端开发
下面是发送交易的代码,channel首先通过sendTransactionProposal
发送提案,获取响应之后,如果所有请求都成功,通过sendTransaction
发送交易。
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);
});
}