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);
    });
    
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 225,337评论 6 524
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,560评论 3 406
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,632评论 0 370
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,219评论 1 303
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,219评论 6 401
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,670评论 1 316
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,018评论 3 431
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 41,000评论 0 280
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,552评论 1 326
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,565评论 3 347
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,692评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,280评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,009评论 3 341
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,435评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,587评论 1 277
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,276评论 3 383
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,752评论 2 367

推荐阅读更多精彩内容