【注意】最后更新于 October 19, 2017,文中内容可能已过时,请谨慎使用。
上一篇讲了在chaincode端函数GetFuncAndParameters()的使用,但是在客户端这块使用还是有一点问题,就在此进一步解释下.
首先我们看下ChaincodeStub
怎么写的
1
2
3
4
5
6
7
8
9
10
11
12
13
|
type ChaincodeStub struct {
TxID string
chaincodeEvent *pb.ChaincodeEvent
args [][]byte
handler *Handler
signedProposal *pb.SignedProposal
proposal *pb.Proposal
// Additional fields extracted from the signedProposal
creator []byte
transient map[string][]byte
binding []byte
}
|
可以很清楚的看到,这里面并没有Function参数,是全部都从args里面提取出来的,那么在java端这个setFcn()
以及setArgs()
是怎么回事呢?
正常情况下,java端操作时的函数如下
1
2
3
4
|
TransactionProposalRequest tpr = client.newTransactionProposalRequest();
tpr.setChaincodeID(chaincodeID);
tpr.setFcn(fcn);
tpr.setArgs(args);
|
这里面是有函数以及参数的,那么进入到chaincode里面就没有了,是怎么变化的呢?
可以看到,发送transactionProposalRequest
是使用的这个方式
1
|
Collection<ProposalResponse> transactionPropResp = channel.sendTransactionProposal(tpr, channel.getPeers());
|
那么就进入这个函数体看一看吧。
1
2
3
|
public Collection<ProposalResponse> sendTransactionProposal(TransactionProposalRequest transactionProposalRequest, Collection<Peer> peers) throws ProposalException, InvalidArgumentException {
return this.sendProposal(transactionProposalRequest, peers);
}
|
调用的内部方法,传递的request
,继续看这个sengProposal
函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
private Collection<ProposalResponse> sendProposal(TransactionRequest proposalRequest, Collection<Peer> peers) throws InvalidArgumentException, ProposalException {
this.checkChannelState();
this.checkPeers(peers);
if (null == proposalRequest) {
throw new InvalidArgumentException("sendProposal queryProposalRequest is null");
} else {
proposalRequest.setSubmitted();
try {
TransactionContext transactionContext = this.getTransactionContext(proposalRequest.getUserContext());
transactionContext.verify(proposalRequest.doVerify());
transactionContext.setProposalWaitTime(proposalRequest.getProposalWaitTime());
ProposalBuilder proposalBuilder = ProposalBuilder.newBuilder();
proposalBuilder.context(transactionContext);
proposalBuilder.request(proposalRequest);
SignedProposal invokeProposal = this.getSignedProposal(transactionContext, proposalBuilder.build());
return this.sendProposalToPeers(peers, invokeProposal, transactionContext);
} catch (ProposalException var6) {
throw var6;
} catch (Exception var7) {
ProposalException exp = new ProposalException(var7);
logger.error(exp.getMessage(), exp);
throw exp;
}
}
}
|
在这里我们可以看到两个有用的地方,一个是
1
|
proposalBuilder.request(proposalRequest);
|
还有一个是
1
|
SignedProposal invokeProposal = this.getSignedProposal(transactionContext, proposalBuilder.build());
|
第一个是给builder
设置request
,第二个是执行builder.build()
操作。看下第一个有没有对fcn
以及args
进行操作的内容。
1
2
3
4
5
6
7
|
public ProposalBuilder request(TransactionRequest request) {
this.request = request;
this.chaincodeID(request.getChaincodeID().getFabricChaincodeID());
this.ccType(request.getChaincodeLanguage() == org.hyperledger.fabric.sdk.TransactionRequest.Type.JAVA ? Type.JAVA : Type.GOLANG);
this.transientMap = request.getTransientMap();
return this;
}
|
可以看到,并没有。那么看build
函数吧
1
2
3
4
5
6
7
|
public Proposal build() throws ProposalException {
if (this.request != null && this.request.noChannelID()) {
this.channelID = "";
}
return this.createFabricProposal(this.channelID, this.chaincodeID);
}
|
有一句话是createFabricProposal
函数,看看是怎么写的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
private Proposal createFabricProposal(String channelID, ChaincodeID chaincodeID) {
if (null == this.transientMap) {
this.transientMap = Collections.emptyMap();
}
if (IS_DEBUG_LEVEL) {
Iterator var3 = this.transientMap.entrySet().iterator();
while(var3.hasNext()) {
Entry<String, byte[]> tme = (Entry)var3.next();
logger.debug(String.format("transientMap('%s', '%s'))", Utils.logString((String)tme.getKey()), Utils.logString(new String((byte[])tme.getValue(), StandardCharsets.UTF_8))));
}
}
ChaincodeHeaderExtension chaincodeHeaderExtension = ChaincodeHeaderExtension.newBuilder().setChaincodeId(chaincodeID).build();
ChannelHeader chainHeader = ProtoUtils.createChannelHeader(HeaderType.ENDORSER_TRANSACTION, this.context.getTxID(), channelID, this.context.getEpoch(), this.context.getFabricTimestamp(), chaincodeHeaderExtension);
ChaincodeInvocationSpec chaincodeInvocationSpec = this.createChaincodeInvocationSpec(chaincodeID, this.ccType);
Map<String, ByteString> bsm = new HashMap(this.transientMap.size());
Iterator var7 = this.transientMap.entrySet().iterator();
while(var7.hasNext()) {
Entry<String, byte[]> tme = (Entry)var7.next();
bsm.put(tme.getKey(), ByteString.copyFrom((byte[])tme.getValue()));
}
ChaincodeProposalPayload payload = ChaincodeProposalPayload.newBuilder().setInput(chaincodeInvocationSpec.toByteString()).putAllTransientMap(bsm).build();
Header header = Header.newBuilder().setSignatureHeader(ProtoUtils.getSignatureHeaderAsByteString(this.context)).setChannelHeader(chainHeader.toByteString()).build();
return Proposal.newBuilder().setHeader(header.toByteString()).setPayload(payload.toByteString()).build();
}
|
貌似没有说?不不不,让我们看下chaincodeInvocationSpec
这个变量是怎么生成的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
private ChaincodeInvocationSpec createChaincodeInvocationSpec(ChaincodeID chaincodeID, Type langType) {
List<ByteString> allArgs = new ArrayList();
Iterator var13;
if (this.argList != null && this.argList.size() > 0) {
allArgs = this.argList;
} else if (this.request != null) {
((List)allArgs).add(ByteString.copyFrom(this.request.getFcn(), StandardCharsets.UTF_8));
List<String> args = this.request.getArgs();
if (args != null && args.size() > 0) {
Iterator var5 = args.iterator();
while(var5.hasNext()) {
String arg = (String)var5.next();
((List)allArgs).add(ByteString.copyFrom(arg.getBytes(StandardCharsets.UTF_8)));
}
}
List<byte[]> argBytes = this.request.getArgBytes();
if (argBytes != null && argBytes.size() > 0) {
var13 = argBytes.iterator();
while(var13.hasNext()) {
byte[] arg = (byte[])var13.next();
((List)allArgs).add(ByteString.copyFrom(arg));
}
}
}
if (IS_DEBUG_LEVEL) {
StringBuilder logout = new StringBuilder(1000);
logout.append(String.format("ChaincodeInvocationSpec type: %s, chaincode name: %s, chaincode path: %s, chaincode version: %s", langType.name(), chaincodeID.getName(), chaincodeID.getPath(), chaincodeID.getVersion()));
String sep = "";
logout.append(" args(");
for(var13 = ((List)allArgs).iterator(); var13.hasNext(); sep = ", ") {
ByteString x = (ByteString)var13.next();
logout.append(sep).append("\"").append(Utils.logString(new String(x.toByteArray(), StandardCharsets.UTF_8))).append("\"");
}
logout.append(")");
logger.debug(logout.toString());
}
ChaincodeInput chaincodeInput = ChaincodeInput.newBuilder().addAllArgs((Iterable)allArgs).build();
ChaincodeSpec chaincodeSpec = ChaincodeSpec.newBuilder().setType(langType).setChaincodeId(chaincodeID).setInput(chaincodeInput).build();
return ChaincodeInvocationSpec.newBuilder().setChaincodeSpec(chaincodeSpec).build();
}
|
有没有看到这两句?
1
2
3
4
5
6
7
8
9
10
11
|
((List)allArgs).add(ByteString.copyFrom(this.request.getFcn(), StandardCharsets.UTF_8));
List<String> args = this.request.getArgs();
if (args != null && args.size() > 0) {
Iterator var5 = args.iterator();
while(var5.hasNext()) {
String arg = (String)var5.next();
((List)allArgs).add(ByteString.copyFrom(arg.getBytes(StandardCharsets.UTF_8)));
}
}
|
看到了吧,这个地方把fcn
和args
合并到一起了,然后作为payload发送出去了。
所以,假设我们需要调chaincode中的addUser
方法,需要4个参数,就应该这么写。
1
2
|
tpr.setFcn("addUser")
tpr.setArgs(new String[]{"a","b","c","d"});
|
好了!客户端函数使用也完工!