Hyperledger Fabric实践:供应链金融案例
背景描述:
在供应链金融产品中,供应商、核心企业、银行、金融机构等多方并存,共同参与交易完成。由于参与方众多,其中涉及很多清算和结算功能,如果采用传统方案解决会产生很多中间环节,导致效率低下。区块链的出现给供应链金融的实现提供了新的解决方案。
案例描述:
案例实现的是简单的“应收账款融资”场景。
业务流程:
1、核心企业与供应商线下签订合同并发货
2、供应商在链上发起供货交易
3、核心企业和金融机构确认并签名交易
4、金融机构发起放款请求给供应商
以上每一笔交易都需要所有参与方认同。
环境配置
演示为三个组织——核心企业、供应商、金融机构。
使用环境为fabric v1.1
IP | 节点 | 域名 | 组织名称 |
---|---|---|---|
10.254.186.164 | orderer | orderer.gyl.com | 排序节点 |
10.254.186.164 | peer | peer0.org1.gyl.com | 供应商 |
10.254.247.165 | peer | peer0.org2.gyl.com | 金融机构 |
10.254.207.154 | peer | peer0.org3.gyl.com | 核心企业 |
合约部分
package mainimport ("fmt""github.com/hyperledger/fabric/core/chaincode/shim"pb "github.com/hyperledger/fabric/protos/peer""encoding/json""github.com/hyperledger/fabric/core/chaincode/lib/cid""crypto/x509""encoding/base64""strconv"
)var asset_time = "asset_name_a"
type scfinancechaincode struct {}/**系统初始化*/// Init callback representing the invocation of a chaincode
func (t *scfinancechaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {fmt.Println("Init success! ")return shim.Success([]byte("Init success !!!!!"))
}/**
系统Invoke方法*/func (t *scfinancechaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {function, args := stub.GetFunctionAndParameters()switch function {case "putvalue":if len(args) != 2 {return shim.Error("putvalue params num err!")}return t.putvalue(stub, args[0], args[1])case "getlastvalue":if len(args) != 1 {return shim.Error("getlastvalue params num err!")}return t.getlastvalue(stub,args[0])case "gethistory":if len(args) != 1 {return shim.Error("gethistory params num err!")}return t.gethistory(stub,args[0])case "creator": //返回调用者信息return t.creator(stub,args)case "creator2"://返回调用者信息,方法2return t.creator2(stub,args)case "getattr"://获取用户的属性return t.getattr(stub,args)case "setquota"://给公司设置贷款最大额度return t.setquota(stub,args)case "checkquota"://查看现在还剩多少信用额度return t.checkquota(stub,args[0])case "loan"://执行贷款逻辑return t.loan(stub,args)case "queryloan"://企业申请贷款return t.queryloan(stub,args)default:return shim.Error("Invalid invoke function name.")}
}//申请贷款
//args: 0:项目(公司)名 1:贷款时长 2:贷款额度
func (t *scfinancechaincode) queryloan(stub shim.ChaincodeStubInterface, args []string) pb.Response {if len(args) != 3 {return shim.Error("setquota params num err!")}projectName := args[0]loanTime := args[1]loanNum := args[2]loanItem := &LoanItem{ProjectName:projectName,LoanTime:loanTime,LoanNum:loanNum,}loanItemStr,e := json.Marshal(loanItem)if e != nil {return shim.Error("queryloan error, json Marshal error")}resp1 := t.putvalue(stub,"query_loan_"+projectName,string(loanItemStr))if resp1.Status != shim.OK {return shim.Error("setquota fail ")}return shim.Success([]byte("success queryloan: " + string(loanItemStr)))
}//设置公司或者项目贷款配额,如果已经设置,则不能再设置了
func (t *scfinancechaincode) setquota(stub shim.ChaincodeStubInterface, args []string) pb.Response {if len(args) != 2 {return shim.Error("setquota params num err!")}key := args[0]value := args[1]fmt.Printf("setquota %s, value is %s\n", key, value)bytes, err := stub.GetState(key)if err != nil {return shim.Error("query fail " + err.Error())}if string(bytes) != "" {return shim.Success([]byte("key: " + key + " has been seted quota and the remaining quota is : " + string(bytes)))}resp1 := t.putvalue(stub,key,value)if resp1.Status != shim.OK {return shim.Error("setquota fail ")}return shim.Success([]byte("success setquota key: " + key + ", value: " + value))
}//查看quota还有多少
//key为项目名或者公司名
func (t *scfinancechaincode) checkquota(stub shim.ChaincodeStubInterface, key string) pb.Response {fmt.Printf("checkquota %s\n", key)resp := t.getlastvalue(stub,key)if resp.Status != shim.OK {return shim.Error("checkquota fail ")}return shim.Success(resp.Payload)
}//银行放款给企业
//args: 0:项目名 1:合同ID 2:贷款时长 3:贷款额度
type LoanItem struct {ProjectName stringLoanId stringLoanTime stringLoanNum string
}func (t *scfinancechaincode) loan(stub shim.ChaincodeStubInterface, args []string) pb.Response {if len(args) != 4 {return shim.Error("loan params number error")}projectName := args[0]loanId := args[1]loanTime := args[2]loanNum := args[3]loanItem := &LoanItem{ProjectName:projectName,LoanTime:loanTime,LoanId:loanId,LoanNum:loanNum,}//先查看是否有额度qutaRemaining := t.checkquota(stub,projectName)if qutaRemaining.Status != shim.OK{return shim.Error("loan error")}quta, err1 := strconv.ParseInt(string(qutaRemaining.Payload), 10, 64)loanN, err2 := strconv.ParseInt(loanNum, 10, 64)if err1 != nil || err2 != nil {return shim.Error("loan error, type error")}//贷款额度超过配额if loanN - quta > 0 {return shim.Success([]byte("quta is not enough, can loan " + string(qutaRemaining.Payload) + " at most"))}resp1 := t.putvalue(stub,"loan_" + projectName,loanNum)if resp1.Status != shim.OK {return shim.Error("loan putvalue fail ")}remainingquta := strconv.FormatInt(quta-loanN,10)resp2 := t.putvalue(stub,projectName,remainingquta)if resp2.Status != shim.OK {return shim.Error("loan putvalue fail ")}loanItemStr,e := json.Marshal(loanItem)if e != nil {return shim.Error("loan error, json Marshal error")}return shim.Success(loanItemStr)
}func (t *scfinancechaincode) putvalue(stub shim.ChaincodeStubInterface, key, value string) pb.Response {fmt.Printf("putvalue %s, value is %s\n", key, value)if err := stub.PutState(key, []byte(value)); err != nil {return shim.Error("putvalue fail " + err.Error())}return shim.Success([]byte("success put key: " + key + ", value: " + value))
}func (t *scfinancechaincode) gethistory(stub shim.ChaincodeStubInterface, key string) pb.Response {fmt.Printf("history %s\n", key)iter, err := stub.GetHistoryForKey(key)defer iter.Close()if err != nil {return shim.Error("query fail " + err.Error())}values := make(map[string]string)for iter.HasNext() {fmt.Printf("next\n")if kv, err := iter.Next(); err == nil {fmt.Printf("id: %s value: %s\n", kv.TxId, kv.Value)values[kv.TxId] = string(kv.Value)}if err != nil {return shim.Error("iterator history fail: " + err.Error())}}bytes, err := json.Marshal(values)if err != nil {return shim.Error("json marshal fail: " + err.Error())}return shim.Success(bytes)
}func (t *scfinancechaincode) getlastvalue(stub shim.ChaincodeStubInterface, key string) pb.Response {fmt.Printf("query %s\n", key)bytes, err := stub.GetState(key)if err != nil {return shim.Error("query fail " + err.Error())}return shim.Success(bytes)
}func (t *scfinancechaincode) getattr(stub shim.ChaincodeStubInterface, args []string) pb.Response{if len(args) != 1 {return shim.Error("parametes's number is wrong")}fmt.Println("get attr: ", args[0])value, ok, err := cid.GetAttributeValue(stub, args[0])if err != nil {return shim.Error("get attr error: " + err.Error())}if ok == false {value = "not found"}bytes, err := json.Marshal(value)if err != nil {return shim.Error("json marshal error: " + err.Error())}return shim.Success(bytes)
}func (t *scfinancechaincode) creator(stub shim.ChaincodeStubInterface, args []string) pb.Response{fmt.Println("creator: ", args)bytes, err := stub.GetCreator()if err != nil {return shim.Error("get creator error: " + err.Error())}return shim.Success(bytes)
}
func (t *scfinancechaincode) creator2(stub shim.ChaincodeStubInterface, args []string) pb.Response{var cinfo struct {ID stringORG stringCERT *x509.Certificate}fmt.Println("creator2: ", args)id, err := cid.GetID(stub)if err != nil {return shim.Error("getid error: " + err.Error())}id_readable, err := base64.StdEncoding.DecodeString(id)if err != nil {return shim.Error("base64 decode error: " + err.Error())}cinfo.ID = string(id_readable)mspid, err := cid.GetMSPID(stub)if err != nil {return shim.Error("getmspid error: " + err.Error())}cinfo.ORG = mspidcert, err := cid.GetX509Certificate(stub)if err != nil {return shim.Error("getX509Cert error: " + err.Error())}cinfo.CERT = certbytes, err := json.Marshal(cinfo)if err != nil {return shim.Error("json marshal error: " + err.Error())}return shim.Success(bytes)
}
func main() {err := shim.Start(new(scfinancechaincode))if err != nil {fmt.Printf("Error starting Simple chaincode: %s", err)}
}
client部分
var path = require('path');
var fs = require('fs');
var util = require('util');
var hfc = require('fabric-client');
var Peer = require('fabric-client/lib/Peer.js');
var EventHub = require('fabric-client/lib/EventHub.js');
var User = require('crypto');
var FabricCAService = require('fabric-ca-client');//var log4js = require('log4js');
//var logger = log4js.getLogger('Helper');
//logger.setLevel('DEBUG');var tempdir = "/home/dc2-user/kongli/fabric-client-js-kvs";let client = new hfc();
let tls_cacerts_content_orderer = fs.readFileSync('./orderer/tls/ca.crt');
let opt_orderer = {pem: Buffer.from(tls_cacerts_content_orderer).toString(),'ssl-target-name-override':'orderer.gyl.com'
};
//peer1
let tls_cacerts_content_peer1 = fs.readFileSync('./peer1/tls/ca.crt');
let opt_peer1 = {pem: Buffer.from(tls_cacerts_content_peer1).toString(),'ssl-target-name-override':'peer0.org1.gyl.com'
};
//peer2
let tls_cacerts_content_peer2 = fs.readFileSync('./peer2/tls/ca.crt');
let opt_peer2 = {pem: Buffer.from(tls_cacerts_content_peer2).toString(),'ssl-target-name-override':'peer0.org2.gyl.com'
};
//peer3
let tls_cacerts_content_peer3 = fs.readFileSync('./peer3/tls/ca.crt');
let opt_peer3 = {pem: Buffer.from(tls_cacerts_content_peer3).toString(),'ssl-target-name-override':'peer0.org3.gyl.com'
};var channel = client.newChannel('gylchannel');
var order = client.newOrderer('grpcs://10.254.186.164:7050',opt_orderer);
channel.addOrderer(order);
var peer1 = client.newPeer('grpcs://10.254.186.164:7051',opt_peer1);
var peer2 = client.newPeer('grpcs://10.254.247.165:7051',opt_peer2);
var peer3 = client.newPeer('grpcs://10.254.207.154:7051',opt_peer3);
channel.addPeer(peer1);
channel.addPeer(peer2);
channel.addPeer(peer3);var event_url = 'grpcs://10.254.186.164:7053';
/**发起交易@returns {Promis.<TResult>}
*/
var sendTransaction = function(chaincodeid, func, chaincode_args, channelId) {var tx_id = null;var payload;return getOrgUser4Local().then((user)=>{tx_id = client.newTransactionID();var request = {chaincodeId: chaincodeid,fcn: func,args: chaincode_args,chainId: channelId,txId: tx_id};return channel.sendTransactionProposal(request);},(err)=>{console.log('error',err);}).then((chaincodeinvokresult)=>{var proposalResponses = chaincodeinvokresult[0];var proposal = chaincodeinvokresult[1];var header = chaincodeinvokresult[2];var all_good = true;for (var i in proposalResponses) {let one_good = false;//成功if (proposalResponses && proposalResponses[0].response && proposalResponses[0].response.status == 200) {one_good = true;console.info('transaction proposal was good');}else {console.error('transaction proposal was bad');}all_good = all_good & one_good;}if (all_good) {console.info(util.format('Successfully sent proposal and received proposalResponses: Status - %s, message - "%s", metadate - "%s", endorsement signature :%s',proposalResponses[0].response.status,proposalResponses[0].response.message,proposalResponses[0].response.payload,proposalResponses[0].endorsement.signature));payload = proposalResponses[0].response.payloadvar request = {proposalResponses: proposalResponses,proposal: proposal,orderer: order,txId: tx_id,header:header};var transactionID = tx_id.getTransactionID();var eventPromises = [];let eh = client.newEventHub();//接下来设置EventHub,用于监听Transaction是否成功写入,这里也是启用了TLSeh.setPeerAddr(event_url,opt_peer1);eh.connect();let txPromise = new Promise((resolve, reject) => {let handle = setTimeout(() => {eh.disconnect();reject();}, 30000);//向EventHub注册事件的处理办法eh.registerTxEvent(transactionID, (tx, code) => {clearTimeout(handle);eh.unregisterTxEvent(transactionID);eh.disconnect();if (code !== 'VALID') {console.error('The transaction was invalid, code = ' + code);reject();} else {console.log('The transaction has been committed on peer ' +eh._ep._endpoint.addr);resolve();}});});eventPromises.push(txPromise);//把背书后的结果发到orderer排序var sendPromise = channel.sendTransaction(request);return Promise.all([sendPromise].concat(eventPromises)).then((results) => {console.log(' event promise all complete and testing complete');return results[0]; // the first returned value is from the 'sendPromise' which is from the 'sendTransaction()' call}).catch((err) => {console.error('Failed to send transaction and get notifications within the timeout period.');return 'Failed to send transaction and get notifications within the timeout period.';});} else {console.error('Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...');return 'Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...';}},(err)=>{console.log('error',err);}).then((response)=>{if (response.status === 'SUCCESS') {console.log('Successfully sent transaction to the orderer.');return util.format("%s",payload);} else {console.error('Failed to order the transaction. Error code: ' + response.status);return 'Failed to order the transaction. Error code: ' + response.status;}},(err)=>{console.log('error',err);});
}/**根据cryptogen模块生成的账号通过Fabric接口进行相关操作@returns {Promise.<TResult>}
*/
function getOrgUser4Local(){var keyPath = "./users/keystore";var keyPEM = Buffer.from(readAllFiles(keyPath)[0]).toString();var certPath = "./users/signcerts";var certPEM = readAllFiles(certPath)[0].toString();return hfc.newDefaultKeyValueStore({path: tempdir}).then((store)=>{client.setStateStore(store);return client.createUser({username: 'user87',mspid: 'GylOrg1MSP',cryptoContent: {privateKeyPEM: keyPEM,signedCertPEM: certPEM}});});
};function readAllFiles(dir) {var files = fs.readdirSync(dir);var certs = [];files.forEach((file_name)=>{let file_path = path.join(dir,file_name);let data = fs.readFileSync(file_path);certs.push(data);});return certs;
}/**获取channel的区块信息@returns {Promise.<TResult>}
*/
var getBlockChainInfo = function() {return getOrgUser4Local().then((user)=>{return channel.queryInfo(peer1);},(err)=>{console.log('error',err);})
}/**根据区块链的编号获取详细信息@param blocknum@returns {Promise.<TResult>}
*/
var getblockInfobyNum = function(blocknum) {return getOrgUser4Local().then((user)=>{return channel.queryBlock(blocknum,peer1,null);},(err)=>{console.log('error',err);})
}/**根据区块链的哈希值获取区块详细信息@param blockhash@returns {Promise.<TResult>}
*/
var getblockInfobyHash = function(blockHash) {return getOrgUser4Local().then((user)=>{return channel.queryBlockByHash(new Buffer(blockHash,"hex"),peer1);},(err)=>{console.log('error',err);})
}
/**获取当前节点加入的通道信息@returns {Promise.<TResult>}
*/
var getPeerChannel = function() {return getOrgUser4Local().then((user)=>{return client.queryChannels(peer1);},(err)=>{console.log('error',err);})
}/**查询指定peer节点已经install的chaincode@returns {Promise.<TResult>}
*/
var getPeerInstallCc = function() {return getOrgUser4Local().then((user)=>{return client.queryInstalledChaincodes(peer1);},(err)=>{console.log('error',err);})
}/**查询指定channel中已实例化的chaincode@returns {Promise.<TResult>}
*/
var getPeerInstantiatedCc = function() {return getOrgUser4Local().then((user)=>{return channel.queryInstantiatedChaincodes(peer1);},(err)=>{console.log('error',err);})
}/**查询指定交易所在区块信息@param txId@returns {Promis.<TResult>}*/
var getBlockByTxID = function(TxID) {return getOrgUser4Local().then((user)=>{return channel.queryBlockByTxID(TxID,peer1);},(err)=>{console.log('error',err);})
}/**查询指定交易所在区块信息@param txId@returns {Promis.<TResult>}*/
var getTransaction = function(TxID) {return getOrgUser4Local().then((user)=>{return channel.queryTransaction(TxID,peer1);},(err)=>{console.log('error',err);})
}exports.sendTransaction = sendTransaction;
exports.getBlockChainInfo = getBlockChainInfo;
exports.getblockInfobyNum = getblockInfobyNum;
exports.getblockInfobyHash = getblockInfobyHash;
exports.getPeerChannel = getPeerChannel;
exports.getPeerInstantiatedCc = getPeerInstantiatedCc;
exports.getPeerInstallCc = getPeerInstallCc;
exports.getBlockByTxID = getBlockByTxID;
exports.getTransaction = getTransaction;
浏览器部分
var co = require('co');
var fabricservice = require('./fabricservice.js');
var express = require('express');var app = express();var channelid = "gylchannel";
var chaincodeid = "gyl";//供应商发起供货交易
app.get('/sendTransaction1',function(req,res){co(function * () {var k = req.query.k;var v = req.query.v;var blockinfo = yield fabricservice.sendTransaction(chaincodeid,"putvalue",[k,v],channelid);res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
});//核心企业发起确认
app.get('/sendTransaction2',function(req,res){co(function * () {var k = req.query.k;var v = req.query.v;var blockinfo = yield fabricservice.sendTransaction(chaincodeid,"putvalue",[k,v],channelid);res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//金融机构审核并放款
app.get('/sendTransaction3',function(req,res){co(function * () {var k = req.query.k;var v = req.query.v;var blockinfo = yield fabricservice.sendTransaction(chaincodeid,"putvalue",[k,v],channelid);res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//企业申请贷款
app.get('/queryloan',function(req,res){co(function * () {var project = req.query.project;var times = req.query.time;var number = req.query.numbervar blockinfo = yield fabricservice.sendTransaction(chaincodeid,"queryloan",[project,times,number],channelid);res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//给公司设置贷款最大额度
app.get('/setquota',function(req,res){co(function * () {var project = req.query.project;var number = req.query.number;var blockinfo = yield fabricservice.sendTransaction(chaincodeid,"setquota",[project,number],channelid);res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//执行贷款逻辑
app.get('/loan',function(req,res){co(function * () {var project = req.query.project;var loanID = req.query.loanID;var times = req.query.time;var number = req.query.number;var blockinfo = yield fabricservice.sendTransaction(chaincodeid,"loan",[project,loanID,times,number],channelid);res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//查看现在还剩多少信用额度
app.get('/checkquota',function(req,res){co(function * () {var project = req.query.project;var blockinfo = yield fabricservice.sendTransaction(chaincodeid,"checkquota",[project],channelid);res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//查询交易记录
app.get('/queryhistory',function(req,res){co(function * () {var k = req.query.k;var blockinfo = yield fabricservice.sendTransaction(chaincodeid,"gethistory",[k],channelid);res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//查询最新结果
app.get('/getlastvalue',function(req,res){co(function * () {var k = req.query.k;var blockinfo = yield fabricservice.sendTransaction(chaincodeid,"getlastvalue",[k],channelid);res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//查看调用者信息1
app.get('/creator',function(req,res){co(function * () {var k = req.query.k;var blockinfo = yield fabricservice.sendTransaction(chaincodeid,"creator",[],channelid);res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//查看调用者信息2
app.get('/creator2',function(req,res){co(function * () {var k = req.query.k;var blockinfo = yield fabricservice.sendTransaction(chaincodeid,"creator2",[],channelid);res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//查看调用者信息属性
app.get('/getattr',function(req,res){co(function * () {var k = req.query.k;var blockinfo = yield fabricservice.sendTransaction(chaincodeid,"getattr",[],channelid);res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//获取当前通道块儿高度
app.get('/getchannelheight',function(req,res){co(function * () {var blockinfo = yield fabricservice.getBlockChainInfo();res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//根据区块编号获取区块信息
app.get('/getblockInfobyNum',function(req,res){co(function * () {var param = parseInt(req.query.params);var blockinfo = yield fabricservice.getblockInfobyNum(param);res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//根据区块Hash值获取区块信息
app.get('/getblockInfobyHash',function(req,res){co(function * () {var param = req.query.params;var blockinfo = yield fabricservice.getblockInfobyHash(param);res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//获取指定peer节点加入的通道数
app.get('/getPeerChannel',function(req,res){co(function * () {var blockinfo = yield fabricservice.getPeerChannel();res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//获取channel已经安装的链码
app.get('/getPeerInstallCc',function(req,res){co(function * () {var blockinfo = yield fabricservice.getPeerInstallCc();res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//获取指定channel已经实例化的链码
app.get('/getPeerInstantiatedCc',function(req,res){co(function * () {var blockinfo = yield fabricservice.getPeerInstantiatedCc();res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//通过交易ID获取区块信息
app.get('/getBlockByTxID',function(req,res){co(function * () {var param = req.query.TxID;var blockinfo = yield fabricservice.getBlockByTxID(param);res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//通过交易ID获取交易信息
app.get('/getTransaction',function(req,res){co(function * () {var param = req.query.TxID;var blockinfo = yield fabricservice.getTransaction(param);res.send(JSON.stringify(blockinfo));}).catch((err)=>{res.send(err);})
})//启动http服务
var server = app.listen(3000,function(){var host = server.address().address;var port = server.address().port;console.log('Example app listen at http://%s:%s',host,port);
});//注册异常处理器
process.on('unhandleRejection',function(err){console.error(err.stack);
});process.on('uncaughtException',console.error);
流程
1、供应商发起供货交易调用,如:
http://116.85.10.181:3000/sendTransaction1?k=food&v=1
2、核心企业发起确认,如:
http://116.85.10.181:3000/sendTransaction2?k=food&v=2
3、银行审核后确认放款,如:
http://116.85.10.181:3000/sendTransaction3?k=food&v=3
目前链码写的比较简单,最好每个组织有自己的链码,组织间约定好操作内容。即使采用同样的链码,由于账本中会记录调用者的身份信息(组织以及签名等),所以假如供应商直接调用http://116.85.10.181:3000/sendTransaction3?k=food&v=3 虽然通过但是账本会记录调用者不是银行机构,可以认定无效。
贷款流程:
1、核心企业abc执行申请贷款10000元:
http://116.85.10.181:3000/queryloan?project=abc&time=20181205&number=10000
2、银行背景审查后给予最大贷款额度20000元:
http://116.85.10.181:3000/setquota?project=abc&number=20000
3、银行执行贷款流程:
http://116.85.10.181:3000/loan?project=abc&loanID=00001&time=20181206&number=10000
注意:
1、需要准备npm环境,fabric-client 需要在v.1.1.0 grpc为v.1.10.1
可以使用npm list xxx 来查看版本信息。
2、使用浏览器访问需要开通端口访问权限。
Hyperledger Fabric实践:供应链金融案例相关推荐
- 万字长文详解供应链金融案例:以菜鸟网络、蚂蚁金服、怡亚通为例
"供应链金融"最大的特点就是在供应链中寻找出一个大的核心企业,以核心企业为出发点,为供应链提供金融支持.一方面,将资金有效注入处于相对弱势的上下游配套中小企业,解决中小企业融资难和 ...
- Hyperledger Fabric 入门必读书籍
下面整理一些 Hyperledger Fabric 一些比较好的区块链书籍,仅供学习参考. 1. 区块链原理.设计与应用 1.1 书籍封面 1.2 介绍 本书由超级账本核心设计和开发者撰写,是区块链开 ...
- 供应链金融智能合约整理
一.solidity合约 Loan.sol · acrowise/Blockchain-FinalProject-SupplyChainFinancialPlatform - Gitee.com ht ...
- 区块链如何破解供应链金融痛点
作者简介: 卿苏德,博士,高级工程师,中国信息通信研究院区块链主管,可信区块链推进计划办公室主任,工业互联网联盟工业区块链特设组主席,国际电信联盟ITU-T FG DLT分布式账本焦点组测试评估负责人 ...
- 泛融等多家业界权威通力合作,联合信通院发布《区块链供应链金融白皮书》
2018年10月31日下午,区块链政策法律研究组成立会暨<区块链与供应链金融白皮书(2018 年)>发布会在中国信息通信研究院举行.会议当天,来自腾讯.百度.京东金融.泛融科技.SAP.西 ...
- [求助]hyperledger fabric在创建peer通道时出现this policy requires 1 of the 'Writers' sub-policies to be……
毕业设计打算做区块链有关的内容,目前买了本机械工业出版社出版的<区块链开发实战 hyperledger Fabric关键技术与案例分析>这本书来学习,可能由于版本问题,在使用书上的配置文件 ...
- Hyperledger Fabric区块链供应链金融实战1
我们在这里将利用Hyperledger Fabric最新版本v2.0.0 Beta,创建一个区块链供应链金融项目,实现应收账款.承兑汇票.合同融资功能.出于学习目的,我们将Hyperledger Fa ...
- 钢铁B2B电商案例:供应链金融如何解决供应链金融痛点
附Java/C/C++/机器学习/算法与数据结构/前端/安卓/Python/程序员必读技术书单大全: 书单导航页(点击右侧小资源即可打开个人博客):技术书栈 =====>>[Java大牛带 ...
- Hyperledger Fabric资产案例-链码实例
案例分析 功能 开户和销户 资产登记,资产上链,与具体的用户绑定 资产转让,资产所有权变更 查询功能,用户查询,资产查询,资产变更的历史查询 业务实体 用户: 名字.身份证(标识).资产列表 资产:名 ...
最新文章
- 自动生成纯文本表格的工具
- Response重定向---javaweb
- 【CyberSecurityLearning 20】xu ni zhuan yong wang luo
- [IoC容器Unity]第三回:依赖注入
- 几种常用的清除浮动方法(一)
- Consolidate Conditional Expression(合并条件表达式)
- Java函数编码_转[收集java的常用函数代码]
- web前端之html从入门到精通
- 先查询后修改并发的时候sql_SQL调优总结
- [SPOJ CIRU]The area of the union of circles(自适应Simpson积分求圆并面积)
- TCP-丢包率【传输中所丢数据包数量占发送数据包的比率=(输入报文-输出报文)/输入报文】【TCP只保证传输层消息可靠,并不保证应用层的消息可靠。想保证应用层的消息可靠性,需应用层自己实现逻辑做保障】
- 订单下单成功后如何等待支付成功
- Google 以图搜图 - 相似图片搜索原理 - Java实现
- 第十二周 静态 +友元 + 动态 + 继承 + 多文件
- 【嵌入式】MSP430系统实时时钟RTC学习日志(完善中)
- Allegro任意形状铜皮倒圆角
- windows作为产品的一些设置和开发(经验之谈)
- 让你流泪的不是毕业,而是无法再重走一次的青春
- 《微型计算机原理与接口技术》复习笔记(一)
- 从25匹马中选5匹最快马
热门文章
- [幽默网文]亚视射雕经典对白
- 扯白 || 从“抄袭狗”到网文大神,那些年我们看过的文都是怎么写出来的【转】
- 用美图秀秀更改照片背景颜色
- 英伟达ASIC实习生面试 [IC]
- JS字符串截取 “指定字符” 前面和后面的内容
- VSCode Eslint+Prettier+Vetur常用配置
- 【学习OpenGL】(三)——3D空间中的点与线
- javascript——数组、数组遍历、forEach、增加删除元素
- 拼网站服务器地址,怎么拼网页服务器ip地址
- 讨教大学|A3 和 8D有什么区别