背景描述:
在供应链金融产品中,供应商、核心企业、银行、金融机构等多方并存,共同参与交易完成。由于参与方众多,其中涉及很多清算和结算功能,如果采用传统方案解决会产生很多中间环节,导致效率低下。区块链的出现给供应链金融的实现提供了新的解决方案。
案例描述:
案例实现的是简单的“应收账款融资”场景。
业务流程:
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 main
import (
"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"://返回调用者信息,方法2
return 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 string
LoanId string
LoanTime string
LoanNum 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 string
ORG string
CERT *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 = mspid
cert, err := cid.GetX509Certificate(stub)
if err != nil {
return shim.Error("getX509Cert error: " + err.Error())
}
cinfo.CERT = cert
bytes, 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.payload
var 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是否成功写入,这里也是启用了TLS
eh.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.number
var 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、使用浏览器访问需要开通端口访问权限。