public CompletableFuture<TransactionEvent> sendTransaction(Collection<? extends ProposalResponse> proposalResponses,
                                                               TransactionOptions transactionOptions) {
        try {

            if (null == transactionOptions) {
                throw new InvalidArgumentException("Parameter transactionOptions can't be null");
            }
            checkChannelState();
            User userContext = transactionOptions.userContext != null ? transactionOptions.userContext : client.getUserContext();
            userContextCheck(userContext);
            if (null == proposalResponses) {
                throw new InvalidArgumentException("sendTransaction proposalResponses was null");
            }

            List<Orderer> orderers = transactionOptions.orderers != null ? transactionOptions.orderers :
                    new ArrayList<>(getOrderers());

            // make certain we have our own copy
            final List<Orderer> shuffeledOrderers = new ArrayList<>(orderers);

            if (transactionOptions.shuffleOrders) {
                Collections.shuffle(shuffeledOrderers);
            }

            if (config.getProposalConsistencyValidation()) {
                HashSet<ProposalResponse> invalid = new HashSet<>();
                int consistencyGroups = SDKUtils.getProposalConsistencySets(proposalResponses, invalid).size();

                if (consistencyGroups != 1 || !invalid.isEmpty()) {
                    throw new IllegalArgumentException(format(
                            "The proposal responses have %d inconsistent groups with %d that are invalid."
                                    + " Expected all to be consistent and none to be invalid.",
                            consistencyGroups, invalid.size()));

                }

            }

            List<FabricProposalResponse.Endorsement> ed = new LinkedList<>();
            FabricProposal.Proposal proposal = null;
            ByteString proposalResponsePayload = null;
            String proposalTransactionID = null;
            TransactionContext transactionContext = null;

            for (ProposalResponse sdkProposalResponse : proposalResponses) {
                ed.add(sdkProposalResponse.getProposalResponse().getEndorsement());
                if (proposal == null) {
                    proposal = sdkProposalResponse.getProposal();
                    proposalTransactionID = sdkProposalResponse.getTransactionID();
                    if (proposalTransactionID == null) {
                        throw new InvalidArgumentException("Proposals with missing transaction ID");
                    }
                    proposalResponsePayload = sdkProposalResponse.getProposalResponse().getPayload();
                    if (proposalResponsePayload == null) {
                        throw new InvalidArgumentException("Proposals with missing payload.");
                    }
                    transactionContext = sdkProposalResponse.getTransactionContext();
                    if (transactionContext == null) {
                        throw new InvalidArgumentException("Proposals with missing transaction context.");
                    }
                } else {
                    final String transactionID = sdkProposalResponse.getTransactionID();
                    if (transactionID == null) {
                        throw new InvalidArgumentException("Proposals with missing transaction id.");
                    }
                    if (!proposalTransactionID.equals(transactionID)) {
                        throw new InvalidArgumentException(format("Proposals with different transaction IDs %s,  and %s", proposalTransactionID, transactionID));
                    }
                }
            }

            TransactionBuilder transactionBuilder = TransactionBuilder.newBuilder();

            Payload transactionPayload = transactionBuilder
                    .chaincodeProposal(proposal)
                    .endorsements(ed)
                    .proposalResponsePayload(proposalResponsePayload).build();

            Envelope transactionEnvelope = createTransactionEnvelope(transactionPayload, transactionContext);

            NOfEvents nOfEvents = transactionOptions.nOfEvents;

            if (nOfEvents == null) {
                nOfEvents = NOfEvents.createNofEvents();
                Collection<Peer> eventingPeers = getEventingPeers();
                boolean anyAdded = false;
                if (!eventingPeers.isEmpty()) {
                    anyAdded = true;
                    nOfEvents.addPeers(eventingPeers);
                }

                if (!anyAdded) {
                    nOfEvents = NOfEvents.createNoEvents();
                }

            } else if (nOfEvents != NOfEvents.nofNoEvents) {
                StringBuilder issues = new StringBuilder(100);
                Collection<Peer> eventingPeers = getEventingPeers();
                nOfEvents.unSeenPeers().forEach(peer -> {
                    if (peer.getChannel() != this) {
                        issues.append(format("Peer %s added to NOFEvents does not belong this channel. ", peer.getName()));

                    } else if (!eventingPeers.contains(peer)) {
                        issues.append(format("Peer %s added to NOFEvents is not a eventing Peer in this channel. ", peer.getName()));
                    }

                });

                if (nOfEvents.unSeenPeers().isEmpty()) {
                    issues.append("NofEvents had no added  Peer eventing services.");
                }
                String foundIssues = issues.toString();
                if (!foundIssues.isEmpty()) {
                    throw new InvalidArgumentException(foundIssues);
                }
            }

            final boolean replyonly = nOfEvents == NOfEvents.nofNoEvents || (getEventingPeers().isEmpty());

            CompletableFuture<TransactionEvent> sret;
            if (replyonly) { //If there are no eventsto complete the future, complete it
                // immediately but give no transaction event
                logger.debug(format("Completing transaction id %s immediately no peer eventing services found in channel %s.", proposalTransactionID, name));
                sret = new CompletableFuture<>();
            } else {
                sret = registerTxListener(proposalTransactionID, nOfEvents, transactionOptions.failFast);
            }

            logger.debug(format("Channel %s sending transaction to orderer(s) with TxID %s ", name, proposalTransactionID));
            boolean success = false;
            Exception lException = null; // Save last exception to report to user .. others are just logged.

            BroadcastResponse resp = null;
            Orderer failed = null;
            for (Orderer orderer : shuffeledOrderers) {
                if (failed != null) {
                    logger.warn(format("Channel %s  %s failed. Now trying %s.", name, failed, orderer));
                }
                failed = orderer;
                try {

                    if (null != diagnosticFileDumper) {
                        logger.trace(format("Sending to channel %s, orderer: %s, transaction: %s", name, orderer.getName(),
                                diagnosticFileDumper.createDiagnosticProtobufFile(transactionEnvelope.toByteArray())));
                    }

                    resp = orderer.sendTransaction(transactionEnvelope);
                    lException = null; // no longer last exception .. maybe just failed.
                    if (resp.getStatus() == Status.SUCCESS) {
                        success = true;
                        break;
                    } else {
                        logger.warn(format("Channel %s %s failed. Status returned %s", name, orderer, getRespData(resp)));
                    }
                } catch (Exception e) {
                    String emsg = format("Channel %s unsuccessful sendTransaction to orderer %s (%s)",
                            name, orderer.getName(), orderer.getUrl());
                    if (resp != null) {

                        emsg = format("Channel %s unsuccessful sendTransaction to orderer %s (%s).  %s",
                                name, orderer.getName(), orderer.getUrl(), getRespData(resp));
                    }

                    logger.error(emsg);
                    lException = new Exception(emsg, e);

                }

            }

            if (success) {
                logger.debug(format("Channel %s successful sent to Orderer transaction id: %s",
                        name, proposalTransactionID));
                if (replyonly) {
                    sret.complete(null); // just say we're done.
                }
                return sret;
            } else {

                String emsg = format("Channel %s failed to place transaction %s on Orderer. Cause: UNSUCCESSFUL. %s",
                        name, proposalTransactionID, getRespData(resp));

                unregisterTxListener(proposalTransactionID);

                CompletableFuture<TransactionEvent> ret = new CompletableFuture<>();
                ret.completeExceptionally(lException != null ? new Exception(emsg, lException) : new Exception(emsg));
                return ret;
            }
        } catch (Exception e) {

            CompletableFuture<TransactionEvent> future = new CompletableFuture<>();
            future.completeExceptionally(e);
            return future;

        }

    }
public CompletableFuture < TransactionEvent > sendTransaction ( Collection < ? extends ProposalResponse > proposalResponses , TransactionOptions transactionOptions ) { try { if ( null == transactionOptions ) { throw new InvalidArgumentException ( "Parameter transactionOptions can't be null" ) ; } checkChannelState ( ) ; User userContext = transactionOptions . userContext != null ? transactionOptions . userContext : client . getUserContext ( ) ; userContextCheck ( userContext ) ; if ( null == proposalResponses ) { throw new InvalidArgumentException ( "sendTransaction proposalResponses was null" ) ; } List < Orderer > orderers = transactionOptions . orderers != null ? transactionOptions . orderers : new ArrayList < > ( getOrderers ( ) ) ; final List < Orderer > shuffeledOrderers = new ArrayList < > ( orderers ) ; if ( transactionOptions . shuffleOrders ) { Collections . shuffle ( shuffeledOrderers ) ; } if ( config . getProposalConsistencyValidation ( ) ) { HashSet < ProposalResponse > invalid = new HashSet < > ( ) ; int consistencyGroups = SDKUtils . getProposalConsistencySets ( proposalResponses , invalid ) . size ( ) ; if ( consistencyGroups != 1 || ! invalid . isEmpty ( ) ) { throw new IllegalArgumentException ( format ( "The proposal responses have %d inconsistent groups with %d that are invalid." + " Expected all to be consistent and none to be invalid." , consistencyGroups , invalid . size ( ) ) ) ; } } List < FabricProposalResponse . Endorsement > ed = new LinkedList < > ( ) ; FabricProposal . Proposal proposal = null ; ByteString proposalResponsePayload = null ; String proposalTransactionID = null ; TransactionContext transactionContext = null ; for ( ProposalResponse sdkProposalResponse : proposalResponses ) { ed . add ( sdkProposalResponse . getProposalResponse ( ) . getEndorsement ( ) ) ; if ( proposal == null ) { proposal = sdkProposalResponse . getProposal ( ) ; proposalTransactionID = sdkProposalResponse . getTransactionID ( ) ; if ( proposalTransactionID == null ) { throw new InvalidArgumentException ( "Proposals with missing transaction ID" ) ; } proposalResponsePayload = sdkProposalResponse . getProposalResponse ( ) . getPayload ( ) ; if ( proposalResponsePayload == null ) { throw new InvalidArgumentException ( "Proposals with missing payload." ) ; } transactionContext = sdkProposalResponse . getTransactionContext ( ) ; if ( transactionContext == null ) { throw new InvalidArgumentException ( "Proposals with missing transaction context." ) ; } } else { final String transactionID = sdkProposalResponse . getTransactionID ( ) ; if ( transactionID == null ) { throw new InvalidArgumentException ( "Proposals with missing transaction id." ) ; } if ( ! proposalTransactionID . equals ( transactionID ) ) { throw new InvalidArgumentException ( format ( "Proposals with different transaction IDs %s,  and %s" , proposalTransactionID , transactionID ) ) ; } } } TransactionBuilder transactionBuilder = TransactionBuilder . newBuilder ( ) ; Payload transactionPayload = transactionBuilder . chaincodeProposal ( proposal ) . endorsements ( ed ) . proposalResponsePayload ( proposalResponsePayload ) . build ( ) ; Envelope transactionEnvelope = createTransactionEnvelope ( transactionPayload , transactionContext ) ; NOfEvents nOfEvents = transactionOptions . nOfEvents ; if ( nOfEvents == null ) { nOfEvents = NOfEvents . createNofEvents ( ) ; Collection < Peer > eventingPeers = getEventingPeers ( ) ; boolean anyAdded = false ; if ( ! eventingPeers . isEmpty ( ) ) { anyAdded = true ; nOfEvents . addPeers ( eventingPeers ) ; } if ( ! anyAdded ) { nOfEvents = NOfEvents . createNoEvents ( ) ; } } else if ( nOfEvents != NOfEvents . nofNoEvents ) { StringBuilder issues = new StringBuilder ( 100 ) ; Collection < Peer > eventingPeers = getEventingPeers ( ) ; nOfEvents . unSeenPeers ( ) . forEach ( peer -> { if ( peer . getChannel ( ) != this ) { issues . append ( format ( "Peer %s added to NOFEvents does not belong this channel. " , peer . getName ( ) ) ) ; } else if ( ! eventingPeers . contains ( peer ) ) { issues . append ( format ( "Peer %s added to NOFEvents is not a eventing Peer in this channel. " , peer . getName ( ) ) ) ; } } ) ; if ( nOfEvents . unSeenPeers ( ) . isEmpty ( ) ) { issues . append ( "NofEvents had no added  Peer eventing services." ) ; } String foundIssues = issues . toString ( ) ; if ( ! foundIssues . isEmpty ( ) ) { throw new InvalidArgumentException ( foundIssues ) ; } } final boolean replyonly = nOfEvents == NOfEvents . nofNoEvents || ( getEventingPeers ( ) . isEmpty ( ) ) ; CompletableFuture < TransactionEvent > sret ; if ( replyonly ) { logger . debug ( format ( "Completing transaction id %s immediately no peer eventing services found in channel %s." , proposalTransactionID , name ) ) ; sret = new CompletableFuture < > ( ) ; } else { sret = registerTxListener ( proposalTransactionID , nOfEvents , transactionOptions . failFast ) ; } logger . debug ( format ( "Channel %s sending transaction to orderer(s) with TxID %s " , name , proposalTransactionID ) ) ; boolean success = false ; Exception lException = null ; BroadcastResponse resp = null ; Orderer failed = null ; for ( Orderer orderer : shuffeledOrderers ) { if ( failed != null ) { logger . warn ( format ( "Channel %s  %s failed. Now trying %s." , name , failed , orderer ) ) ; } failed = orderer ; try { if ( null != diagnosticFileDumper ) { logger . trace ( format ( "Sending to channel %s, orderer: %s, transaction: %s" , name , orderer . getName ( ) , diagnosticFileDumper . createDiagnosticProtobufFile ( transactionEnvelope . toByteArray ( ) ) ) ) ; } resp = orderer . sendTransaction ( transactionEnvelope ) ; lException = null ; if ( resp . getStatus ( ) == Status . SUCCESS ) { success = true ; break ; } else { logger . warn ( format ( "Channel %s %s failed. Status returned %s" , name , orderer , getRespData ( resp ) ) ) ; } } catch ( Exception e ) { String emsg = format ( "Channel %s unsuccessful sendTransaction to orderer %s (%s)" , name , orderer . getName ( ) , orderer . getUrl ( ) ) ; if ( resp != null ) { emsg = format ( "Channel %s unsuccessful sendTransaction to orderer %s (%s).  %s" , name , orderer . getName ( ) , orderer . getUrl ( ) , getRespData ( resp ) ) ; } logger . error ( emsg ) ; lException = new Exception ( emsg , e ) ; } } if ( success ) { logger . debug ( format ( "Channel %s successful sent to Orderer transaction id: %s" , name , proposalTransactionID ) ) ; if ( replyonly ) { sret . complete ( null ) ; } return sret ; } else { String emsg = format ( "Channel %s failed to place transaction %s on Orderer. Cause: UNSUCCESSFUL. %s" , name , proposalTransactionID , getRespData ( resp ) ) ; unregisterTxListener ( proposalTransactionID ) ; CompletableFuture < TransactionEvent > ret = new CompletableFuture < > ( ) ; ret . completeExceptionally ( lException != null ? new Exception ( emsg , lException ) : new Exception ( emsg ) ) ; return ret ; } } catch ( Exception e ) { CompletableFuture < TransactionEvent > future = new CompletableFuture < > ( ) ; future . completeExceptionally ( e ) ; return future ; } }
Send transaction to one of a specified set of orderers with the specified user context.
IF there are no eventing peers this future returns immediately completed
indicating that orderer has accepted the transaction only.

@param proposalResponses
@param transactionOptions
@return Future allowing access to the result of the transaction invocation.