node.js - Hyperledger Fabric Node.js SDK : Issue with offline transaction signatures

标签 node.js hyperledger-fabric hyperledger hyperledger-fabric-sdk-js

我正在尝试遵循本教程: https://hyperledger.github.io/fabric-sdk-node/release-1.4/tutorial-sign-transaction-offline.html

我使用本指南创建了一个完整的 Hyperledger Fabric 网络: https://hyperledger-fabric-ca.readthedocs.io/en/latest/operations_guide.html

Javascript SDK 运行良好,我可以使用 client.setUserContext() 方法查询/调用交易。

当尝试离线签署交易时,交易提案被同行拒绝,并显示以下响应:

{ Error: 2 UNKNOWN: access denied: channel [mychannel] creator org [org1]
      at Object.exports.createStatusError ([...]/node_modules/grpc/src/common.js:91:15)
      at Object.onReceiveStatus ([...]/node_modules/grpc/src/client_interceptors.js:1204:28)
      at InterceptingListener._callNext ([...]/node/node_modules/grpc/src/client_interceptors.js:568:42)
      at InterceptingListener.onReceiveStatus ([...]/node/node_modules/grpc/src/client_interceptors.js:618:8)
      at callback ([...]/node/node_modules/grpc/src/client_interceptors.js:845:24)
    code: 2,
    metadata: Metadata { _internal_repr: {} },
    details: 'access denied: channel [mychannel] creator org [org1]' }

在同行的日志中,我看到以下错误:

peer1-org1       | 2020-01-13 21:47:31.569 UTC [protoutils] ValidateProposalMessage -> WARN 078 channel [mychannel]: creator's signature over the proposal is not valid: The signature is invalid

我不明白为什么签名被拒绝。我仔细地遵循了教程的所有步骤。这是完整的客户端代码:

//
// Imports
//
var Client = require('fabric-client');
var path = require('path');
var util = require('util');
var fs = require('fs');
const crypto = require('crypto');
const elliptic = require('elliptic');
const { KEYUTIL } = require('jsrsasign');
const config = require('./config');

//
// Script configuartion variables
//
var fcn = 'set';
var args = ["a","60"];
var priv = fs.readFileSync(config.PRIV, 'utf8');
var { prvKeyHex } = KEYUTIL.getKey(priv,'passphrase'); 
var cert = fs.readFileSync(config.CERT, 'utf8');
const EC = elliptic.ec;
const ecdsaCurve = elliptic.curves['p256'];
const ecdsa = new EC(ecdsaCurve);
const signKey = ecdsa.keyFromPrivate(prvKeyHex, 'hex');


//
// Config init
//
var client = Client.loadFromConfig('network_org1.yaml');
var targets = client.getPeersForOrg('org1');

// 
// Main
// 
client.initCredentialStores()
.then((nothing) => {

channel = client.getChannel(config.CHANNEL_NAME);

// 1. Generate unsigned transaction proposal
var transaction_proposal = {
  chaincodeId: config.CHAINCODE_NAME,
  channelId: config.CHANNEL_NAME,
  fcn: fcn,
  args: args,
};

var { proposal, tx_id } = channel.generateUnsignedProposal(transaction_proposal, 'org1', cert);

// 2. Hash the transaction proposal
var proposalBytes = proposal.toBuffer();
var digest = client.getCryptoSuite().hash(proposalBytes);
//const hash = crypto.createHash('sha256');
//hash.update(proposalBytes);
//var digest = hash.digest('hex');

// 3. Calculate the signature for this transacton proposal
console.log("digest: "+digest);   
console.log("signKey: ");
console.log(util.inspect(signKey));
var sig = ecdsa.sign(Buffer.from(digest, 'hex'), signKey);
var signature = Buffer.from(sig.toDER());
var signedProposal = {
  signature,
  proposal_bytes: proposalBytes,
};

// 4. Send the signed transaction proposal
var proposal_request = {
  signedProposal,
  targets
}
channel.sendSignedProposal(proposal_request)
.then((proposalResponses) => {
    console.log('Proposal responses:');
    console.log(util.inspect(proposalResponses));

    // TODO: Understand why the proposal signature is rejected by the peers

    // 5. Generate unsigned transaction
    var transaction_request = {
      proposalResponses,
      proposal,
    };
    return channel.generateUnsignedTransaction(transaction_request);
})  
.then((commitProposal) => {

    // 6. Sign the unsigned transaction
    var transactionBytes = commitProposal.toBuffer();
    var transaction_digest = client.getCryptoSuite().hash(transactionBytes);
    var transaction_sig = ecdsa.sign(Buffer.from(transaction_digest, 'hex'), signKey);        
    var transaction_signature = Buffer.from(transaction_sig.toDER());

    var signedTransaction = {
      signedProposal: transaction_signature,
      request: transaction_request
    }

    // 7. Commit the signed transaction
    return channel.sendSignedTransaction(signedTransaction);
})
.then((response) => {
    console.log('Successfully sent transaction');
    console.log('Return code: '+response.status);
});

});

任何可能成功离线签署交易的人提供的任何帮助都将是非常棒的。

最佳答案

回答我自己的问题。我的代码中有 2 个问题。

第一个与需要在 Fabric 中以非常特定的方式完成的实际签名相关。我必须创建此函数并将其应用于签名以避免出现问题:

function _preventMalleability(sig) {
const halfOrder = elliptic.curves.p256.n.shrn(1);
if (sig.s.cmp(halfOrder) === 1) {
    const bigNum = elliptic.curves.p256.n;
    sig.s = bigNum.sub(sig.s);
}
return sig;

第 2 个与各种提案/请求/交易结构的格式相关:

transaction_request = {
  proposalResponses: proposalResponses,
  proposal: proposal,
}

signedTransactionProposal = {
    signature: transaction_signature,
    proposal_bytes: transactionBytes,
}

signedTransaction = {
  signedProposal: signedTransactionProposal,
  request: transaction_request,
}

我在这里创建了一个完整的(工作)教程,其中包含详细说明: https://gitlab.com/guillaume.goutaudier/wisekeydemo

关于node.js - Hyperledger Fabric Node.js SDK : Issue with offline transaction signatures,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59725180/

相关文章:

hyperledger - Hyperledger Composer和Hyperledger Fabric之间的区别?

hyperledger-fabric - channel 未配置任何具有 'discover' 角色的对等点

hyperledger-fabric - super 账本结构 : Why doesn't cryptogen generate all the necessary crypto material to register new users?

hyperledger-fabric - 将新对等点添加到 super 账本结构组织中需要采取哪些步骤?

node.js - ioredis 错误 : connect ETIMEDOUT - Can't get connection to local redis server

node.js - 连接 Electron 与sqlite3

docker - 调用链码函数时出现 ENDORSEMENT_POLICY_FAILURE 错误

node.js - 使用 Visual Studio Code 在 Docker 中远程调试 NodeJS

javascript - 读取未定义的 JSON 文件(异步)

hyperledger - 许可的私有(private)区 block 链与中心化系统有何不同?