zoukankan      html  css  js  c++  java
  • Hyperledger Fabric——balance transfer(五)执行交易

    链码安装和实例化之后就可以调用chaincode执行交易,下面分析简单的账户转账操作是如何完成的。

    源码分析

    1.首先看app.js的路由函数

    app.post('/channels/:channelName/chaincodes/:chaincodeName', async function(req, res) {
        var peers = req.body.peers;
        var chaincodeName = req.params.chaincodeName;
        var channelName = req.params.channelName;
        var fcn = req.body.fcn;
        var args = req.body.args;
        // 此处省略了参数校验
        let message = await invoke.invokeChaincode(peers, channelName, chaincodeName, fcn, args, req.username, req.orgname);
        res.send(message);
    });

    2.接下来找到

    var invokeChaincode = async function(peerNames, channelName, chaincodeName, fcn, args, username, org_name) {
        var error_message = null;
        var tx_id_string = null;
        try {
            var client = await helper.getClientForOrg(org_name, username);          // 创建client对象
            var channel = client.getChannel(channelName);                           // 创建channel对象
            // 获取交易id:基于client对象上分配的用户身份(_userContext)
            var tx_id = client.newTransactionID();
            tx_id_string = tx_id.getTransactionID();
            // 请求结构
            var request = {
                targets: peerNames,
                chaincodeId: chaincodeName,
                fcn: fcn,
                args: args,
                chainId: channelName,
                txId: tx_id
            };
            // SDK根据request生成proposal,并调用sendPeersProposal()
            // 将提案发送给背书节点,然后将返回的提案响应连同交易提案一起打包返回给client 
            let results = await channel.sendTransactionProposal(request);
    
            // 返回的交易提案和提案响应 
            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('invoke chaincode proposal was good');
                } else {
                    logger.error('invoke chaincode proposal was bad');
                }
                all_good = all_good & one_good;
            }
    
            if (all_good) {
                var promises = [];
                // 基于channel的事件中心
                let event_hubs = channel.getChannelEventHubsForOrg();
                #event_hubs.forEach((eh) => {
                    #let invokeEventPromise = new Promise((resolve, reject) => {
                        #let event_timeout = setTimeout(() => {
                            let message = 'REQUEST_TIMEOUT:' + eh.getPeerAddr();
                            logger.error(message);
                            eh.disconnect();
                        }, 3000);
                        // 注册交易事件监听,当交易被peer提交到账本中时可以得到反馈
                        #eh.registerTxEvent(tx_id_string, (tx, code, block_num) => {
                            clearTimeout(event_timeout);
                        }, (err) => {
                            clearTimeout(event_timeout);
                            logger.error(err);
                            reject(err);
                        },
                            {unregister: true, disconnect: true}
                        );
                        eh.connect();
                    });
                    promises.push(invokeEventPromise);
                });
                // 将txID,交易提案和提案响应打包成交易请求
                var orderer_request = {
                    txId: tx_id,
                    proposalResponses: proposalResponses,
                    proposal: proposal
                };
                // 将交易请求发送给orderer节点,内部调用sendBroadcast(envelope)
                var sendPromise = channel.sendTransaction(orderer_request);
                promises.push(sendPromise);
                let results = await Promise.all(promises);
    
                let response = results.pop(); 
                if (response.status === 'SUCCESS') {
                    logger.info('Successfully sent transaction to the orderer.');
                } else {
                    error_message = util.format('Failed to order the transaction. Error code: %s',response.status);
                    logger.debug(error_message);
                }
    
            } else {
                error_message = util.format('Failed to send Proposal and receive all good ProposalResponse');
                logger.debug(error_message);
            }
        } catch (error) {
            logger.error('Failed to invoke due to error: ' + error.stack ? error.stack : error);
            error_message = error.toString();
        }
    
        if (!error_message) {
            // 返回交易id
            return tx_id_string;
        } else {
            let message = util.format('Failed to invoke chaincode. cause:%s',error_message);
            logger.error(message);
            throw new Error(message);
        }
    };

    3.如果我们进行简单的转账交易 A->B,则需要调用链码中的move方法,代码如下:

    func (t *SimpleChaincode) move(stub shim.ChaincodeStubInterface, args []string) pb.Response {
        // 两个账户A,B
        var A, B string    
        // 账户的余额数
        var Aval, Bval int 
        // 转移的数值
        var X int         
        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]
    
        // 获取账户A的值
        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))
        // 获取账户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))
    
        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
    ", Aval, Bval)
    
        // 将改变后的值写入状态数据库中
        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);
    }

    测试

    交易:

    curl -s -X POST 
      http://localhost:4000/channels/mychannel/chaincodes/mycc 
      -H "authorization: Bearer <Token>" 
      -H "content-type: application/json" 
      -d '{
        "peers": ["peer0.org1.example.com","peer1.org1.example.com"],
        "fcn":"move",
        "args":["a","b","10"]
    }'

    结果:

    Transacton ID is 970928c6acbae452a43cbc59e1cb9a558a09c4f354cda0025bd51dafcca3ad96
  • 相关阅读:
    JavaScript入门二
    JavaScript入门
    CSS样式之补充
    CSS样式之操作属性二
    隔空手势操作
    项目管理培训(2)
    uoot启动过程
    new work
    库函数开发步骤 (转)
    keil(持续更新)
  • 原文地址:https://www.cnblogs.com/zhayujie/p/12941553.html
Copyright © 2011-2022 走看看