上一篇讲了在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)));
                }
            
}

看到了吧,这个地方把fcnargs合并到一起了,然后作为payload发送出去了。 所以,假设我们需要调chaincode中的addUser方法,需要4个参数,就应该这么写。

1
2
tpr.setFcn("addUser")
tpr.setArgs(new String[]{"a","b","c","d"});

好了!客户端函数使用也完工!