我可以通过在 Hyperledger Fabric 中使用 fabric-python-sdk 一次将 tx 提案发送给 3 个背书节点吗?
Can I send tx proposal to 3 endorsing peers at once by using fabric-python-sdk in Hyperledger Fabric?
- 环境
- Hyperledger Fabric v1.4.7 / fabric-python-sdk
- Node1-Org1(10.10.0.1)、Node2-Org2(10.10.0.2)、Node3-Org3(10.10.0.3)
- 背书政策:
AND ('Org1MSP.peer','Org2MSP.peer','Org3MSP.peer')
- network.json
{
"name": "net_fabric",
"version": "1.0.0",
"client": {
"organization": "Org1",
"credentialStore": {
"path": "/tmp/hfc-kvs",
"cryptoStore": {
"path": "/tmp/hfc-cvs"
},
"wallet": "wallet-name"
},
"connection": {
"timeout": {
"peer": {
"endorser": "60000",
"eventHub": "60000",
"eventReg": "60000"
},
"orderer": "60000"
}
}
},
"channels": {
"mychannel": {
"orderers": [
"orderer.example.com"
],
"peers": {
"peer0.org1.example.com": {
"endorsingPeer": true,
"chaincodeQuery": true,
"eventSource": true
},
"peer1.org1.example.com": {
"endorsingPeer": true,
"chaincodeQuery": true,
"eventSource": true
},
"peer0.org2.example.com": {
"endorsingPeer": true,
"chaincodeQuery": true,
"eventSource": true
},
"peer1.org2.example.com": {
"endorsingPeer": true,
"chaincodeQuery": true,
"eventSource": true
},
"peer0.org3.example.com": {
"endorsingPeer": true,
"chaincodeQuery": true,
"eventSource": true
},
"peer1.org3.example.com": {
"endorsingPeer": true,
"chaincodeQuery": true,
"eventSource": true
}
}
}
},
"organizations": {
"orderer.example.com": {
"mspid": "OrdererMSP",
"orderers": [
"orderer.example.com"
],
"users": {
"Admin": {
"cert": "crypto-config/ordererOrganizations/example.com/users/Admin@example.com/msp/signcerts/Admin@example.com-cert.pem",
"private_key": "crypto-config/ordererOrganizations/example.com/users/Admin@example.com/msp/keystore/bb970e6666e63f00039c25c15f82ac39860027ea3a353fa9152dedf508a68541_sk"
}
}
},
"org1.example.com": {
"mspid": "Org1MSP",
"peers": [
"peer0.org1.example.com",
"peer1.org1.example.com"
],
"certificateAuthorities": [
"ca.org1.example.com"
],
"users": {
"Admin": {
"cert": "crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem",
"private_key": "crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore/9ed12a80062621bc68bcf6d73309cb216b6d72b8b4e4ae2aea5751d69bbead35_sk"
},
"User1": {
"cert": "crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/signcerts/User1@org1.example.com-cert.pem",
"private_key": "crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/keystore/1a476c5cc5874ae5a9b5c313e6fbd4cf4717d2730d4e0349668bc53ef4e05b93_sk"
}
}
},
"org2.example.com": {
"mspid": "Org2MSP",
"peers": [
"peer0.org2.example.com",
"peer1.org2.example.com"
],
"certificateAuthorities": [
"ca.org2.example.com"
],
"users": {
"Admin": {
"cert": "crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/signcerts/Admin@org2.example.com-cert.pem",
"private_key": "crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/keystore/5d3223ea7355959ffe90185e149395ee972902bd6f4ac4ec240c855a2d61ed4a_sk"
},
"User1": {
"cert": "crypto-config/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp/signcerts/User1@org2.example.com-cert.pem",
"private_key": "crypto-config/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp/keystore/57b1ca82356ba68fdf93aa208da3c25f8055310dffacd61373946f2673bc4e96_sk"
}
}
},
"org3.example.com": {
"mspid": "Org3MSP",
"peers": [
"peer0.org3.example.com",
"peer1.org3.example.com"
],
"certificateAuthorities": [
"ca.org3.example.com"
],
"users": {
"Admin": {
"cert": "crypto-config/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp/signcerts/Admin@org3.example.com-cert.pem",
"private_key": "crypto-config/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp/keystore/570a370bfd2cea9ced433be8955c6676a6208d3ec5953df267781cf5ba6bf831_sk"
},
"User1": {
"cert": "crypto-config/peerOrganizations/org3.example.com/users/User1@org3.example.com/msp/signcerts/User1@org3.example.com-cert.pem",
"private_key": "crypto-config/peerOrganizations/org3.example.com/users/User1@org3.example.com/msp/keystore/e24c3bde2038a04bab26453083fd07433070e3cffccad5205ae319ce4d64fc47_sk"
}
}
}
},
"orderers": {
"orderer.example.com": {
"url": "10.10.0.1:7050",
"grpcOptions": {
"grpc.ssl_target_name_override": "orderer.example.com",
"grpc-max-send-message-length": 15
}
}
},
"peers": {
"peer0.org1.example.com": {
"url": "grpc://peer0.org1.example.com:7051",
"grpcOptions": {
"grpc.ssl_target_name_override": "peer0.org1.example.com",
"grpc-max-send-message-length": 15
}
},
"peer1.org1.example.com": {
"url": "grpc://peer1.org1.example.com:7051",
"grpcOptions": {
"grpc.ssl_target_name_override": "peer1.org1.example.com",
"grpc-max-send-message-length": 15
}
},
"peer0.org2.example.com": {
"url": "grpc://peer0.org2.example.com:7051",
"grpcOptions": {
"grpc.ssl_target_name_override": "peer0.org2.example.com",
"grpc-max-send-message-length": 15
}
},
"peer1.org2.example.com": {
"url": "grpc://peer1.org2.example.com:7051",
"grpcOptions": {
"grpc.ssl_target_name_override": "peer1.org2.example.com",
"grpc-max-send-message-length": 15
}
},
"peer0.org3.example.com": {
"url": "grpc://peer0.org3.example.com:7051",
"grpcOptions": {
"grpc.ssl_target_name_override": "peer0.org3.example.com",
"grpc-max-send-message-length": 15
}
},
"peer1.org3.example.com": {
"url": "grpc://peer1.org3.example.com:7051",
"grpcOptions": {
"grpc.ssl_target_name_override": "peer1.org3.example.com",
"grpc-max-send-message-length": 15
}
}
},
"certificateAuthorities": {
"ca.org1.example.com": {
"url": "http://ca.org1.example.com:7054",
"caName": "ca.org1.example.com"
},
"ca.org2.example.com": {
"url": "http://ca.org2.example.com:7054",
"caName": "ca.org2.example.com"
},
"ca.org3.example.com": {
"url": "http://ca.org3.example.com:7054",
"caName": "ca.org3.example.com"
}
}
}
我想一次将 tx 提案发送给 3 个背书节点。
我正在使用 fabric-sdk-py
& grpc
发送这样的交易:
class GrpclbServicer(grpclb_pb2_grpc.GrpclbServicer):
def Execute(self, request, context):
org1_admin = cli.get_user(org_name='org1.example.com', name='Admin')
# The response should be true if succeed
response = loop.run_until_complete(cli.chaincode_invoke(
requestor=org1_admin,
channel_name=request.channelName,
peers=['peer0.org1.example.com','peer0.org2.example.com','peer0.org3.example.com'],
fcn=request.functionName, # createCar
args=request.args,
cc_name=request.chaincodeName, # fabcar
transient_map=None, # optional, for private data
wait_for_event=True, # for being sure chaincode invocation has been commited in the ledger, default is on tx event
))
return grpclb_pb2.TxResponse(
response = response
)
执行chaincode_invoke
时,只能选择一个请求(org1_admin),所以我得到这样的错误信息。 (org1_admin 仅包含 Org1MSP)
2020-09-02 07:13:25.782 UTC [endorser] SimulateProposal -> ERRO 095 [mychannel][4bd53729] failed to invoke chaincode name:"fabcar" , error: txid: 4bd53729be43a4de95d5d6383fc9f6e2f610f05ea486815f75799839d1a8186c(mychannel) exists
所以我改了代码如下:
def Execute(self, request, context):
orgs = ['org1.example.com','org2.example.com','org3.example.com']
# The response should be true if succeed
for org in orgs:
org_admin = cli.get_user(org_name=org, name='Admin')
response = loop.run_until_complete(cli.chaincode_invoke(
requestor=org_admin,
channel_name=request.channelName,
peers=['peer0.'+org],
fcn=request.functionName,
args=request.args,
cc_name=request.chaincodeName,
transient_map=None, # optional, for private data
wait_for_event=True, # for being sure chaincode invocation has been commited in the ledger, default is on tx event
))
但是我遇到了这个错误。我觉得...这个错误的原因是不满足背书策略因为tx是一个一个发送的
2020-09-02 07:29:09.205 UTC [vscc] Validate -> ERRO 06a VSCC error: stateBasedValidator.Validate failed, err validation of endorsement policy for chaincode fabcar in tx 6:0 failed: signature set did not satisfy policy
2020-09-02 07:29:09.205 UTC [committer.txvalidator] validateTx -> ERRO 06b VSCCValidateTx for transaction txId = 3ac7c00db44090924edb8bc88c543430e3b77e69856a8a7c22b7c9062eb2b4d8 returned error: validation of endorsement policy for chaincode fabcar in tx 6:0 failed: signature set did not satisfy policy
有没有办法使用 fabric-sdk-py 立即将 Tx 提案发送给背书节点(peer0.org1、peer0.org2、peer0.org3)?
或者如何满足背书政策?
这是Fabric(Golang)的一个内部方法,但是你可以看到他们的做法,同时向背书人提议source
// processProposals sends a signed proposal to a set of peers, and gathers all the responses.
func processProposals(endorserClients []pb.EndorserClient, signedProposal *pb.SignedProposal) ([]*pb.ProposalResponse, error) {
responsesCh := make(chan *pb.ProposalResponse, len(endorserClients))
errorCh := make(chan error, len(endorserClients))
wg := sync.WaitGroup{}
for _, endorser := range endorserClients {
wg.Add(1)
go func(endorser pb.EndorserClient) { <=== This line creates a new thread for each endorser call
defer wg.Done()
proposalResp, err := endorser.ProcessProposal(context.Background(), signedProposal)
if err != nil {
errorCh <- err
return
}
responsesCh <- proposalResp
}(endorser)
}
wg.Wait() <=== And then wait until receiving all the responses
close(responsesCh)
close(errorCh)
for err := range errorCh {
return nil, err
}
var responses []*pb.ProposalResponse
for response := range responsesCh {
responses = append(responses, response)
}
return responses, nil
}
我个人认为您也可以用 Python 做同样的事情
- 环境
- Hyperledger Fabric v1.4.7 / fabric-python-sdk
- Node1-Org1(10.10.0.1)、Node2-Org2(10.10.0.2)、Node3-Org3(10.10.0.3)
- 背书政策:
AND ('Org1MSP.peer','Org2MSP.peer','Org3MSP.peer')
- network.json
{
"name": "net_fabric",
"version": "1.0.0",
"client": {
"organization": "Org1",
"credentialStore": {
"path": "/tmp/hfc-kvs",
"cryptoStore": {
"path": "/tmp/hfc-cvs"
},
"wallet": "wallet-name"
},
"connection": {
"timeout": {
"peer": {
"endorser": "60000",
"eventHub": "60000",
"eventReg": "60000"
},
"orderer": "60000"
}
}
},
"channels": {
"mychannel": {
"orderers": [
"orderer.example.com"
],
"peers": {
"peer0.org1.example.com": {
"endorsingPeer": true,
"chaincodeQuery": true,
"eventSource": true
},
"peer1.org1.example.com": {
"endorsingPeer": true,
"chaincodeQuery": true,
"eventSource": true
},
"peer0.org2.example.com": {
"endorsingPeer": true,
"chaincodeQuery": true,
"eventSource": true
},
"peer1.org2.example.com": {
"endorsingPeer": true,
"chaincodeQuery": true,
"eventSource": true
},
"peer0.org3.example.com": {
"endorsingPeer": true,
"chaincodeQuery": true,
"eventSource": true
},
"peer1.org3.example.com": {
"endorsingPeer": true,
"chaincodeQuery": true,
"eventSource": true
}
}
}
},
"organizations": {
"orderer.example.com": {
"mspid": "OrdererMSP",
"orderers": [
"orderer.example.com"
],
"users": {
"Admin": {
"cert": "crypto-config/ordererOrganizations/example.com/users/Admin@example.com/msp/signcerts/Admin@example.com-cert.pem",
"private_key": "crypto-config/ordererOrganizations/example.com/users/Admin@example.com/msp/keystore/bb970e6666e63f00039c25c15f82ac39860027ea3a353fa9152dedf508a68541_sk"
}
}
},
"org1.example.com": {
"mspid": "Org1MSP",
"peers": [
"peer0.org1.example.com",
"peer1.org1.example.com"
],
"certificateAuthorities": [
"ca.org1.example.com"
],
"users": {
"Admin": {
"cert": "crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem",
"private_key": "crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore/9ed12a80062621bc68bcf6d73309cb216b6d72b8b4e4ae2aea5751d69bbead35_sk"
},
"User1": {
"cert": "crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/signcerts/User1@org1.example.com-cert.pem",
"private_key": "crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/keystore/1a476c5cc5874ae5a9b5c313e6fbd4cf4717d2730d4e0349668bc53ef4e05b93_sk"
}
}
},
"org2.example.com": {
"mspid": "Org2MSP",
"peers": [
"peer0.org2.example.com",
"peer1.org2.example.com"
],
"certificateAuthorities": [
"ca.org2.example.com"
],
"users": {
"Admin": {
"cert": "crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/signcerts/Admin@org2.example.com-cert.pem",
"private_key": "crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/keystore/5d3223ea7355959ffe90185e149395ee972902bd6f4ac4ec240c855a2d61ed4a_sk"
},
"User1": {
"cert": "crypto-config/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp/signcerts/User1@org2.example.com-cert.pem",
"private_key": "crypto-config/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp/keystore/57b1ca82356ba68fdf93aa208da3c25f8055310dffacd61373946f2673bc4e96_sk"
}
}
},
"org3.example.com": {
"mspid": "Org3MSP",
"peers": [
"peer0.org3.example.com",
"peer1.org3.example.com"
],
"certificateAuthorities": [
"ca.org3.example.com"
],
"users": {
"Admin": {
"cert": "crypto-config/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp/signcerts/Admin@org3.example.com-cert.pem",
"private_key": "crypto-config/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp/keystore/570a370bfd2cea9ced433be8955c6676a6208d3ec5953df267781cf5ba6bf831_sk"
},
"User1": {
"cert": "crypto-config/peerOrganizations/org3.example.com/users/User1@org3.example.com/msp/signcerts/User1@org3.example.com-cert.pem",
"private_key": "crypto-config/peerOrganizations/org3.example.com/users/User1@org3.example.com/msp/keystore/e24c3bde2038a04bab26453083fd07433070e3cffccad5205ae319ce4d64fc47_sk"
}
}
}
},
"orderers": {
"orderer.example.com": {
"url": "10.10.0.1:7050",
"grpcOptions": {
"grpc.ssl_target_name_override": "orderer.example.com",
"grpc-max-send-message-length": 15
}
}
},
"peers": {
"peer0.org1.example.com": {
"url": "grpc://peer0.org1.example.com:7051",
"grpcOptions": {
"grpc.ssl_target_name_override": "peer0.org1.example.com",
"grpc-max-send-message-length": 15
}
},
"peer1.org1.example.com": {
"url": "grpc://peer1.org1.example.com:7051",
"grpcOptions": {
"grpc.ssl_target_name_override": "peer1.org1.example.com",
"grpc-max-send-message-length": 15
}
},
"peer0.org2.example.com": {
"url": "grpc://peer0.org2.example.com:7051",
"grpcOptions": {
"grpc.ssl_target_name_override": "peer0.org2.example.com",
"grpc-max-send-message-length": 15
}
},
"peer1.org2.example.com": {
"url": "grpc://peer1.org2.example.com:7051",
"grpcOptions": {
"grpc.ssl_target_name_override": "peer1.org2.example.com",
"grpc-max-send-message-length": 15
}
},
"peer0.org3.example.com": {
"url": "grpc://peer0.org3.example.com:7051",
"grpcOptions": {
"grpc.ssl_target_name_override": "peer0.org3.example.com",
"grpc-max-send-message-length": 15
}
},
"peer1.org3.example.com": {
"url": "grpc://peer1.org3.example.com:7051",
"grpcOptions": {
"grpc.ssl_target_name_override": "peer1.org3.example.com",
"grpc-max-send-message-length": 15
}
}
},
"certificateAuthorities": {
"ca.org1.example.com": {
"url": "http://ca.org1.example.com:7054",
"caName": "ca.org1.example.com"
},
"ca.org2.example.com": {
"url": "http://ca.org2.example.com:7054",
"caName": "ca.org2.example.com"
},
"ca.org3.example.com": {
"url": "http://ca.org3.example.com:7054",
"caName": "ca.org3.example.com"
}
}
}
我想一次将 tx 提案发送给 3 个背书节点。
我正在使用 fabric-sdk-py
& grpc
发送这样的交易:
class GrpclbServicer(grpclb_pb2_grpc.GrpclbServicer):
def Execute(self, request, context):
org1_admin = cli.get_user(org_name='org1.example.com', name='Admin')
# The response should be true if succeed
response = loop.run_until_complete(cli.chaincode_invoke(
requestor=org1_admin,
channel_name=request.channelName,
peers=['peer0.org1.example.com','peer0.org2.example.com','peer0.org3.example.com'],
fcn=request.functionName, # createCar
args=request.args,
cc_name=request.chaincodeName, # fabcar
transient_map=None, # optional, for private data
wait_for_event=True, # for being sure chaincode invocation has been commited in the ledger, default is on tx event
))
return grpclb_pb2.TxResponse(
response = response
)
执行chaincode_invoke
时,只能选择一个请求(org1_admin),所以我得到这样的错误信息。 (org1_admin 仅包含 Org1MSP)
2020-09-02 07:13:25.782 UTC [endorser] SimulateProposal -> ERRO 095 [mychannel][4bd53729] failed to invoke chaincode name:"fabcar" , error: txid: 4bd53729be43a4de95d5d6383fc9f6e2f610f05ea486815f75799839d1a8186c(mychannel) exists
所以我改了代码如下:
def Execute(self, request, context):
orgs = ['org1.example.com','org2.example.com','org3.example.com']
# The response should be true if succeed
for org in orgs:
org_admin = cli.get_user(org_name=org, name='Admin')
response = loop.run_until_complete(cli.chaincode_invoke(
requestor=org_admin,
channel_name=request.channelName,
peers=['peer0.'+org],
fcn=request.functionName,
args=request.args,
cc_name=request.chaincodeName,
transient_map=None, # optional, for private data
wait_for_event=True, # for being sure chaincode invocation has been commited in the ledger, default is on tx event
))
但是我遇到了这个错误。我觉得...这个错误的原因是不满足背书策略因为tx是一个一个发送的
2020-09-02 07:29:09.205 UTC [vscc] Validate -> ERRO 06a VSCC error: stateBasedValidator.Validate failed, err validation of endorsement policy for chaincode fabcar in tx 6:0 failed: signature set did not satisfy policy
2020-09-02 07:29:09.205 UTC [committer.txvalidator] validateTx -> ERRO 06b VSCCValidateTx for transaction txId = 3ac7c00db44090924edb8bc88c543430e3b77e69856a8a7c22b7c9062eb2b4d8 returned error: validation of endorsement policy for chaincode fabcar in tx 6:0 failed: signature set did not satisfy policy
有没有办法使用 fabric-sdk-py 立即将 Tx 提案发送给背书节点(peer0.org1、peer0.org2、peer0.org3)?
或者如何满足背书政策?
这是Fabric(Golang)的一个内部方法,但是你可以看到他们的做法,同时向背书人提议source
// processProposals sends a signed proposal to a set of peers, and gathers all the responses.
func processProposals(endorserClients []pb.EndorserClient, signedProposal *pb.SignedProposal) ([]*pb.ProposalResponse, error) {
responsesCh := make(chan *pb.ProposalResponse, len(endorserClients))
errorCh := make(chan error, len(endorserClients))
wg := sync.WaitGroup{}
for _, endorser := range endorserClients {
wg.Add(1)
go func(endorser pb.EndorserClient) { <=== This line creates a new thread for each endorser call
defer wg.Done()
proposalResp, err := endorser.ProcessProposal(context.Background(), signedProposal)
if err != nil {
errorCh <- err
return
}
responsesCh <- proposalResp
}(endorser)
}
wg.Wait() <=== And then wait until receiving all the responses
close(responsesCh)
close(errorCh)
for err := range errorCh {
return nil, err
}
var responses []*pb.ProposalResponse
for response := range responsesCh {
responses = append(responses, response)
}
return responses, nil
}
我个人认为您也可以用 Python 做同样的事情