Hyperledger Fabric SDK 示例fabric-samples-《balance-transfer》之六《执行chaincode》

本文已在我的公众号Fabric技术分享原创首发。
转载请标明出处:
https://blog.csdn.net/qq_27818541/article/details/78416848
本文出自:【BigManing的博客】

前言

这里要实现类似转账功能,A->B, 最终要执行的是chaincode的move方法。这里我们看下chaincodemove方法:


func (t *SimpleChaincode) move(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    // must be an invoke
    //1 这里可以理解成 两个账户
    var A, B string    // Entities
    //2 分别对应的资产值
    var Aval, Bval int // Asset holdings
    var X int          // Transaction value
    var err error

    if len(args) != 3 {
        return shim.Error("Incorrect number of arguments. Expecting 4, function followed by 2 names and 1 value")
    }

    A = args[0]
    B = args[1]

    //3 先获取A账户的资产值   Get the state from the ledger
    Avalbytes, err := stub.GetState(A)
    if err != nil {
        return shim.Error("Failed to get state")
    }
    if Avalbytes == nil {
        return shim.Error("Entity not found")
    }
    Aval, _ = strconv.Atoi(string(Avalbytes))

    //4 获取B账户的资产值 
    Bvalbytes, err := stub.GetState(B)
    if err != nil {
        return shim.Error("Failed to get state")
    }
    if Bvalbytes == nil {
        return shim.Error("Entity not found")
    }
    Bval, _ = strconv.Atoi(string(Bvalbytes))

    //5 根据入参  进行账户之间资产的转移
    X, err = strconv.Atoi(args[2])
    if err != nil {
        return shim.Error("Invalid transaction amount, expecting a integer value")
    }
    Aval = Aval - X
    Bval = Bval + X
    logger.Infof("Aval = %d, Bval = %d\n", Aval, Bval)

    //6 最后把最新资产值写入账簿 (类似map的存储方式 k-v)  Write the state back to the ledger
    err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
    if err != nil {
        return shim.Error(err.Error())
    }

    err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
    if err != nil {
        return shim.Error(err.Error())
    }

        return shim.Success(nil);
}

路由

app.js

//在指定的peer上执行交易  Invoke transaction on chaincode on target peers
app.post('/channels/:channelName/chaincodes/:chaincodeName', function(req, res) {
    logger.debug('==================== INVOKE ON CHAINCODE ==================');
    //1 参数校验    
    //执行交易所在的节点
    var peers = req.body.peers;
    //chaincode名称
    var chaincodeName = req.params.chaincodeName;
    //channel名称
    var channelName = req.params.channelName;
    //chaincode方法
    var fcn = req.body.fcn;
    //方法参数
    var args = req.body.args;
    logger.debug('channelName  : ' + channelName);
    logger.debug('chaincodeName : ' + chaincodeName);
    logger.debug('fcn  : ' + fcn);
    logger.debug('args  : ' + args);
    if (!chaincodeName) {
        res.json(getErrorMessage('\'chaincodeName\''));
        return;
    }
    if (!channelName) {
        res.json(getErrorMessage('\'channelName\''));
        return;
    }
    if (!fcn) {
        res.json(getErrorMessage('\'fcn\''));
        return;
    }
    if (!args) {
        res.json(getErrorMessage('\'args\''));
        return;
    }
    //2 具体实现
    invoke.invokeChaincode(peers, channelName, chaincodeName, fcn, args, req.username, req.orgname)
    .then(function(message) {
        res.send(message);
    });
});

具体实现

这里面会涉及到fabric执行交易的基本流程:

invoke-transaction.js

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 helper = require('./helper.js');
var logger = helper.getLogger('invoke-chaincode');
var EventHub = require('fabric-client/lib/EventHub.js');
var ORGS = hfc.getConfigSetting('network-config');

var invokeChaincode = function (peerNames, channelName, chaincodeName, fcn, args, username, org) {
    logger.debug(util.format('\n============ invoke transaction on organization %s ============\n', org));
    var client = helper.getClientForOrg(org);
    var channel = helper.getChannelForOrg(org);
    var targets = (peerNames) ? helper.newPeers(peerNames, org) : undefined;
    var tx_id = null;

    //1  获取 jim 用户( 内部会设置为上下文环境)
    return helper.getRegisteredUsers(username, org).then((user) => {
        tx_id = client.newTransactionID();
        logger.debug(util.format('Sending transaction "%j"', tx_id));

        //2 封装交易提案请求
        var request = {
            chaincodeId: chaincodeName,
            fcn: fcn,
            args: args,
            chainId: channelName,
            txId: tx_id
        };

        if (targets)
            request.targets = targets;
        //3 发送交易提案给背书节点
        // send proposal to endorser
        return channel.sendTransactionProposal(request);
    }, (err) => {
        logger.error('Failed to enroll user \'' + username + '\'. ' + err);
        throw new Error('Failed to enroll user \'' + username + '\'. ' + err);
    }).then((results) => {
        //4 处理背书结果
        var proposalResponses = results[0];
        var proposal = results[1];
        var all_good = true;
        for (var i in proposalResponses) {
            let one_good = false;
            if (proposalResponses && proposalResponses[i].response &&
                proposalResponses[i].response.status === 200) {
                one_good = true;
                logger.info('transaction proposal was good');
            } else {
                logger.error('transaction proposal was bad');
            }
            all_good = all_good & one_good;
        }
        if (all_good) {
            logger.debug(util.format(
                'Successfully sent Proposal and received ProposalResponse: Status - %s, message - "%s", metadata - "%s", endorsement signature: %s',
                proposalResponses[0].response.status, proposalResponses[0].response.message,
                proposalResponses[0].response.payload, proposalResponses[0].endorsement
                    .signature));
            //5 如果背书结果ok  封装正式交易请求
            var request = {
                proposalResponses: proposalResponses,
                proposal: proposal
            };
            // set the transaction listener and set a timeout of 30sec
            // if the transaction did not get committed within the timeout period,
            // fail the test
            var transactionID = tx_id.getTransactionID();
            var eventPromises = [];

            if (!peerNames) {
                peerNames = channel.getPeers().map(function (peer) {
                    return peer.getName();
                });
            }
            //5.1  设置事件监听 并生成带有超时设置的promise
            var eventhubs = helper.newEventHubs(peerNames, org);
            for (let key in eventhubs) {
                let eh = eventhubs[key];
                eh.connect();

                let txPromise = new Promise((resolve, reject) => {
                    let handle = setTimeout(() => {
                        eh.disconnect();
                        reject();
                    }, 30000);

                    eh.registerTxEvent(transactionID, (tx, code) => {
                        clearTimeout(handle);
                        eh.unregisterTxEvent(transactionID);
                        eh.disconnect();

                        if (code !== 'VALID') {
                            logger.error(
                                'The balance transfer transaction was invalid, code = ' + code);
                            reject();
                        } else {
                            logger.info(
                                'The balance transfer transaction has been committed on peer ' +
                                eh._ep._endpoint.addr);
                            resolve();
                        }
                    });
                });
                eventPromises.push(txPromise);
            };
            //6 发送交易
            var sendPromise = channel.sendTransaction(request);
            //7  集合所有的promise 并执行
            return Promise.all([sendPromise].concat(eventPromises)).then((results) => {
                logger.debug(' 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) => {
                logger.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 {
            logger.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) => {
        logger.error('Failed to send proposal due to error: ' + err.stack ? err.stack :
            err);
        return 'Failed to send proposal due to error: ' + err.stack ? err.stack :
            err;
    }).then((response) => {
        //8 处理第七步返回的结果
        if (response.status === 'SUCCESS') {
            logger.info('Successfully sent transaction to the orderer.');
            return tx_id.getTransactionID();
        } else {
            logger.error('Failed to order the transaction. Error code: ' + response.status);
            return 'Failed to order the transaction. Error code: ' + response.status;
        }
    }, (err) => {
        logger.error('Failed to send transaction due to error: ' + err.stack ? err
            .stack : err);
        return 'Failed to send transaction due to error: ' + err.stack ? err.stack :
            err;
    });
};

exports.invokeChaincode = invokeChaincode;

基本流程

Created with Raphaël 2.1.2 Start 获取到当前用户 封装交易提案请求 发送提案request给背书节点 所有的背书响应是否ok 封装交易请求 发送交易,最终调用move方法 处理结果 End yes no

API访问

echo "POST invoke chaincode on peers of Org1 and Org2"
echo
TRX_ID=$(curl -s -X POST \
  http://localhost:4000/channels/mychannel/chaincodes/mycc \
  -H "authorization: Bearer $ORG1_TOKEN" \
  -H "content-type: application/json" \
  -d '{
    "fcn":"move",
    "args":["a","b","10"]
}')
echo "Transacton ID is $TRX_ID"
echo
echo

控制台打印:


Transacton ID is eb058850e7247ad0bc25ec57ced1cd1255666c390ca0c9300858ce6e57175221

后台打印:


[2017-10-16 11:07:40.822] [DEBUG] SampleWebApp - Decoded from JWT token: username - Jim, orgname - org1
[2017-10-16 11:07:40.823] [DEBUG] SampleWebApp - ==================== INVOKE ON CHAINCODE ==================

// 后台获取到的参数
[2017-10-16 11:07:40.823] [DEBUG] SampleWebApp - channelName  : mychannel
[2017-10-16 11:07:40.823] [DEBUG] SampleWebApp - chaincodeName : mycc
[2017-10-16 11:07:40.823] [DEBUG] SampleWebApp - fcn  : move
[2017-10-16 11:07:40.823] [DEBUG] SampleWebApp - args  : a,b,10
[2017-10-16 11:07:40.824] [DEBUG] invoke-chaincode - 
============ invoke transaction on organization org1 ============

// 设置Jim为当前的上下文环境
[2017-10-16 11:07:40.824] [DEBUG] Helper - [FileKeyValueStore.js]: FileKeyValueStore.js - constructor
[2017-10-16 11:07:40.825] [DEBUG] Helper - [FileKeyValueStore.js]: FileKeyValueStore -- getValue
[2017-10-16 11:07:40.826] [DEBUG] Helper - [crypto_ecdsa_aes]: importKey - start
[2017-10-16 11:07:40.826] [DEBUG] Helper - [crypto_ecdsa_aes]: importKey - have the key [Circular]
[2017-10-16 11:07:40.827] [DEBUG] Helper - [utils.CryptoKeyStore]: _getKeyStore resolving store
[2017-10-16 11:07:40.828] [DEBUG] Helper - [ecdsa/key.js]: ECDSA curve param X: b3e2a75b9a4fd2b081589b1c45bde49f583aea9d7d466798c7b765c4ca96973d
[2017-10-16 11:07:40.828] [DEBUG] Helper - [ecdsa/key.js]: ECDSA curve param Y: c58820e5a9db75ab17841ac158e9389ad0d130a6827dc0682dcb2d2c5dccb349
[2017-10-16 11:07:40.829] [DEBUG] Helper - [FileKeyValueStore.js]: FileKeyValueStore -- setValue
[2017-10-16 11:07:40.831] [DEBUG] Helper - [utils.CryptoKeyStore]: _getKeyStore resolving store
[2017-10-16 11:07:40.831] [DEBUG] Helper - [FileKeyValueStore.js]: FileKeyValueStore -- getValue
[2017-10-16 11:07:40.832] [DEBUG] Helper - [ecdsa/key.js]: ECDSA curve param X: b3e2a75b9a4fd2b081589b1c45bde49f583aea9d7d466798c7b765c4ca96973d
[2017-10-16 11:07:40.832] [DEBUG] Helper - [ecdsa/key.js]: ECDSA curve param Y: c58820e5a9db75ab17841ac158e9389ad0d130a6827dc0682dcb2d2c5dccb349
[2017-10-16 11:07:40.832] [DEBUG] Helper - [ecdsa/key.js]: ECDSA curve param X: b3e2a75b9a4fd2b081589b1c45bde49f583aea9d7d466798c7b765c4ca96973d
[2017-10-16 11:07:40.832] [DEBUG] Helper - [ecdsa/key.js]: ECDSA curve param Y: c58820e5a9db75ab17841ac158e9389ad0d130a6827dc0682dcb2d2c5dccb349
[2017-10-16 11:07:40.832] [DEBUG] Helper - [ecdsa/key.js]: ECDSA curve param X: b3e2a75b9a4fd2b081589b1c45bde49f583aea9d7d466798c7b765c4ca96973d
[2017-10-16 11:07:40.832] [DEBUG] Helper - [ecdsa/key.js]: ECDSA curve param Y: c58820e5a9db75ab17841ac158e9389ad0d130a6827dc0682dcb2d2c5dccb349
[2017-10-16 11:07:40.832] [DEBUG] Helper - [FileKeyValueStore.js]: FileKeyValueStore -- setValue
[2017-10-16 11:07:40.833] [INFO] Helper - Successfully loaded member from persistence

// 发送交易提案
[2017-10-16 11:07:40.833] [DEBUG] invoke-chaincode - Sending transaction "{"_nonce":{"type":"Buffer","data":[169,122,192,203,43,77,68,42,128,240,8,81,90,35,63,156,13,236,81,59,126,148,200,17]},"_transaction_id":"eb058850e7247ad0bc25ec57ced1cd1255666c390ca0c9300858ce6e57175221"}"
[2017-10-16 11:07:40.841] [DEBUG] Helper - [crypto_ecdsa_aes]: ecdsa signature:  Signature {
  r: <BN: 2a552dc46d1921f3bacd95df82cc38c855946021a93a7b8cba61ee9b4cdb9857>,
  s: <BN: 4877817703a392a85561824acfcf695ec09a37d8032c2d52afddccfae2063d22>,
  recoveryParam: 0 }
[2017-10-16 11:07:40.855] [INFO] invoke-chaincode - transaction proposal was good
[2017-10-16 11:07:40.855] [INFO] invoke-chaincode - transaction proposal was good

// 交易提案的响应
[2017-10-16 11:07:40.855] [DEBUG] invoke-chaincode - Successfully sent Proposal and received ProposalResponse: Status - 200, message - "OK", metadata - "", endorsement signature: 0D ?�cEW�>��N���p����r�[b�gaR5f�@ `"���D��}������0�s0�6��:�%�v/í

//设置监听事件

info: [EventHub.js]: _connect - options {"grpc.ssl_target_name_override":"peer0.org1.example.com","grpc.default_authority":"peer0.org1.example.com"}
[2017-10-16 11:07:40.861] [DEBUG] Helper - [crypto_ecdsa_aes]: ecdsa signature:  Signature {
  r: <BN: c3b1ae588f3356ce1caf07d06d26ca92ccd4255a38e99c41cce9c51513ba61c1>,
  s: <BN: 7747f104839f5f40e9cbef787513341c3de7cf87da18fb24fbdf786b3d7dc191>,
  recoveryParam: 0 }
info: [EventHub.js]: _connect - options 
{"grpc.ssl_target_name_override":"peer1.org1.example.com","grpc.default_authority":"peer1.org1.example.com"}
[2017-10-16 11:07:40.867] [DEBUG] Helper - [crypto_ecdsa_aes]: ecdsa signature:  Signature {
  r: <BN: c3b1ae588f3356ce1caf07d06d26ca92ccd4255a38e99c41cce9c51513ba61c1>,
  s: <BN: 7747f104839f5f40e9cbef787513341c3de7cf87da18fb24fbdf786b3d7dc191>,
  recoveryParam: 0 }
[2017-10-16 11:07:40.875] [DEBUG] Helper - [crypto_ecdsa_aes]: ecdsa signature:  Signature {
  r: <BN: b11a259870e0bbf072584f353ab1515f4759c854eb475bedccba38b5c362a4d7>,
  s: <BN: 66796e05eae9015e63eea208708a9a8e91853821c7221a843c4bd07debfae97c>,
  recoveryParam: 1 }
[2017-10-16 11:07:43.022] [DEBUG] Helper - [crypto_ecdsa_aes]: ecdsa signature:  Signature {
  r: <BN: b516e7e14243395b12a21a51c864c0b65f442d57501b07cce481892b8e2c7caa>,
  s: <BN: cff64d35bdd6b9a10bec5e5bd5b90f2adcb8f576b77c28d7317d4d0c003fb3d>,
  recoveryParam: 1 }


[2017-10-16 11:07:43.022] [INFO] invoke-chaincode - The balance transfer transaction has been committed on peer localhost:7058
[2017-10-16 11:07:43.045] [DEBUG] Helper - [crypto_ecdsa_aes]: ecdsa signature:  Signature {
  r: <BN: b516e7e14243395b12a21a51c864c0b65f442d57501b07cce481892b8e2c7caa>,
  s: <BN: cff64d35bdd6b9a10bec5e5bd5b90f2adcb8f576b77c28d7317d4d0c003fb3d>,
  recoveryParam: 1 }


[2017-10-16 11:07:43.045] [INFO] invoke-chaincode - The balance transfer transaction has been committed on peer localhost:7053
[2017-10-16 11:07:43.046] [DEBUG] invoke-chaincode -  event promise all complete and testing complete

// 交易会由orderer打包成区块,最终由peer写入各自的账本
[2017-10-16 11:07:43.046] [INFO] invoke-chaincode - Successfully sent transaction to the orderer.

猜你喜欢

转载自blog.csdn.net/qq_27818541/article/details/78416848