org.hyperledger.fabric.sdk.ProposalResponse.java Source code

Java tutorial

Introduction

Here is the source code for org.hyperledger.fabric.sdk.ProposalResponse.java

Source

/*
 Copyright IBM Corp. All Rights Reserved.
    
 SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.fabric.sdk;

import java.lang.ref.WeakReference;

import javax.xml.bind.DatatypeConverter;

import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperledger.fabric.protos.common.Common;
import org.hyperledger.fabric.protos.common.Common.Header;
import org.hyperledger.fabric.protos.ledger.rwset.Rwset.TxReadWriteSet;
import org.hyperledger.fabric.protos.msp.Identities;
import org.hyperledger.fabric.protos.peer.FabricProposal;
import org.hyperledger.fabric.protos.peer.FabricProposal.ChaincodeHeaderExtension;
import org.hyperledger.fabric.protos.peer.FabricProposalResponse;
import org.hyperledger.fabric.sdk.exception.CryptoException;
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
import org.hyperledger.fabric.sdk.exception.ProposalException;
import org.hyperledger.fabric.sdk.helper.Config;
import org.hyperledger.fabric.sdk.helper.DiagnosticFileDumper;
import org.hyperledger.fabric.sdk.security.CryptoSuite;
import org.hyperledger.fabric.sdk.transaction.TransactionContext;

import static java.lang.String.format;
import static org.hyperledger.fabric.sdk.helper.Utils.toHexString;

public class ProposalResponse extends ChaincodeResponse {

    private static final Log logger = LogFactory.getLog(ProposalResponse.class);
    private static final Config config = Config.getConfig();
    private static final boolean IS_TRACE_LEVEL = logger.isTraceEnabled();

    private static final DiagnosticFileDumper diagnosticFileDumper = IS_TRACE_LEVEL
            ? config.getDiagnosticFileDumper()
            : null;

    private boolean isVerified = false;
    private boolean hasBeenVerified = false;

    private WeakReference<ProposalResponsePayloadDeserializer> proposalResponsePayload;
    private FabricProposal.Proposal proposal;
    private FabricProposalResponse.ProposalResponse proposalResponse;
    private Peer peer = null;
    private ChaincodeID chaincodeID = null;
    private final TransactionContext transactionContext;

    ProposalResponse(TransactionContext transactionContext, int status, String message) {
        super(transactionContext.getTxID(), transactionContext.getChannelID(), status, message);
        this.transactionContext = transactionContext;
    }

    TransactionContext getTransactionContext() {
        return transactionContext;
    }

    ProposalResponsePayloadDeserializer getProposalResponsePayloadDeserializer() throws InvalidArgumentException {
        if (isInvalid()) {
            throw new InvalidArgumentException("Proposal response is invalid.");
        }

        ProposalResponsePayloadDeserializer ret = null;

        if (proposalResponsePayload != null) {
            ret = proposalResponsePayload.get();

        }
        if (ret == null) {

            try {
                ret = new ProposalResponsePayloadDeserializer(proposalResponse.getPayload());
            } catch (Exception e) {
                throw new InvalidArgumentException(e);
            }

            proposalResponsePayload = new WeakReference<>(ret);
        }

        return ret;

    }

    ByteString getPayloadBytes() {
        return proposalResponse.getPayload();

    }

    public boolean isVerified() {
        return isVerified;
    }

    /*
     * Verifies that a Proposal response is properly signed. The payload is the
     * concatenation of the response payload byte string and the endorsement The
     * certificate (public key) is gotten from the Endorsement.Endorser.IdBytes
     * field
     *
     * @param crypto the CryptoPrimitives instance to be used for signing and
     * verification
     *
     * @return true/false depending on result of signature verification
     */
    boolean verify(CryptoSuite crypto) {
        logger.trace(format("%s verifying transaction: %s endorsement.", peer, getTransactionID()));

        if (hasBeenVerified) { // check if this proposalResponse was already verified   by client code
            logger.trace(format("%s transaction: %s was already verified returned %b", peer, getTransactionID(),
                    isVerified));
            return this.isVerified;
        }

        try {
            if (isInvalid()) {
                this.isVerified = false;
                logger.debug(format("%s for transaction %s returned invalid. Setting verify to false", peer,
                        getTransactionID()));
                return false;
            }

            FabricProposalResponse.Endorsement endorsement = this.proposalResponse.getEndorsement();
            ByteString sig = endorsement.getSignature();
            byte[] endorserCertifcate = null;
            byte[] signature = null;
            byte[] data = null;

            try {
                Identities.SerializedIdentity endorser = Identities.SerializedIdentity
                        .parseFrom(endorsement.getEndorser());
                ByteString plainText = proposalResponse.getPayload().concat(endorsement.getEndorser());

                if (config.extraLogLevel(10)) {

                    if (null != diagnosticFileDumper) {
                        StringBuilder sb = new StringBuilder(10000);
                        sb.append("payload TransactionBuilderbytes in hex: "
                                + DatatypeConverter.printHexBinary(proposalResponse.getPayload().toByteArray()));
                        sb.append("\n");
                        sb.append("endorser bytes in hex: "
                                + DatatypeConverter.printHexBinary(endorsement.getEndorser().toByteArray()));
                        sb.append("\n");
                        sb.append("plainText bytes in hex: "
                                + DatatypeConverter.printHexBinary(plainText.toByteArray()));

                        logger.trace("payload TransactionBuilderbytes:  "
                                + diagnosticFileDumper.createDiagnosticFile(sb.toString()));
                    }

                }

                if (sig == null || sig.isEmpty()) { // we shouldn't get here ...
                    logger.warn(format("%s %s returned signature is empty verify set to false.", peer,
                            getTransactionID()));
                    this.isVerified = false;
                } else {

                    endorserCertifcate = endorser.getIdBytes().toByteArray();
                    signature = sig.toByteArray();
                    data = plainText.toByteArray();

                    this.isVerified = crypto.verify(endorserCertifcate, config.getSignatureAlgorithm(), signature,
                            data);
                    if (!this.isVerified) {
                        logger.warn(format(
                                "%s transaction: %s verify: Failed to verify. Endorsers certificate: %s, "
                                        + "signature: %s, signing algorithm: %s, signed data: %s.",
                                peer, getTransactionID(), toHexString(endorserCertifcate), toHexString(signature),
                                config.getSignatureAlgorithm(), toHexString(data)));
                    }
                }

            } catch (InvalidProtocolBufferException | CryptoException e) {
                logger.error(format(
                        "%s transaction: %s verify: Failed to verify. Endorsers certificate: %s, "
                                + "signature: %s, signing algorithm: %s, signed data: %s.",
                        peer, getTransactionID(), toHexString(endorserCertifcate), toHexString(signature),
                        config.getSignatureAlgorithm(), toHexString(data)), e);

                logger.error(format(
                        "%s transaction: %s verify: Cannot retrieve peer identity from ProposalResponse. Error is: %s",
                        peer, getTransactionID(), e.getMessage()), e);
                this.isVerified = false;
            }

            logger.debug(format("%s finished verify for transaction %s returning %b", peer, getTransactionID(),
                    this.isVerified));

            return this.isVerified;
        } finally {
            hasBeenVerified = true;
        }
    } // verify

    public FabricProposal.Proposal getProposal() {
        return proposal;
    }

    public void setProposal(FabricProposal.SignedProposal signedProposal) throws ProposalException {

        try {
            this.proposal = FabricProposal.Proposal.parseFrom(signedProposal.getProposalBytes());
        } catch (InvalidProtocolBufferException e) {
            throw new ProposalException(format("%s transaction: %s Proposal exception", peer, getTransactionID()),
                    e);

        }
    }

    /**
     * Get response to the proposal returned by the peer.
     *
     * @return peer response.
     */

    public FabricProposalResponse.ProposalResponse getProposalResponse() {
        return proposalResponse;
    }

    public void setProposalResponse(FabricProposalResponse.ProposalResponse proposalResponse) {
        this.proposalResponse = proposalResponse;
    }

    /**
     * The peer this proposal was created on.
     *
     * @return See {@link Peer}
     */

    public Peer getPeer() {
        return this.peer;
    }

    void setPeer(Peer peer) {
        this.peer = peer;
    }

    //    public ByteString getPayload() {
    //        return proposalResponse.getPayload();
    //    }

    /**
     * Chaincode ID that was executed.
     *
     * @return See {@link ChaincodeID}
     * @throws InvalidArgumentException
     */

    public ChaincodeID getChaincodeID() throws InvalidArgumentException {

        try {

            if (chaincodeID == null) {

                Header header = Header.parseFrom(proposal.getHeader());
                Common.ChannelHeader channelHeader = Common.ChannelHeader.parseFrom(header.getChannelHeader());
                ChaincodeHeaderExtension chaincodeHeaderExtension = ChaincodeHeaderExtension
                        .parseFrom(channelHeader.getExtension());
                chaincodeID = new ChaincodeID(chaincodeHeaderExtension.getChaincodeId());
            }
            return chaincodeID;

        } catch (Exception e) {
            throw new InvalidArgumentException(e);
        }

    }

    /**
     * ChaincodeActionResponsePayload is the result of the executing chaincode.
     *
     * @return the result of the executing chaincode.
     * @throws InvalidArgumentException
     */

    public byte[] getChaincodeActionResponsePayload() throws InvalidArgumentException {

        if (isInvalid()) {
            throw new InvalidArgumentException("Proposal response is invalid.");
        }

        try {

            final ProposalResponsePayloadDeserializer proposalResponsePayloadDeserializer = getProposalResponsePayloadDeserializer();
            ByteString ret = proposalResponsePayloadDeserializer.getExtension().getChaincodeAction().getResponse()
                    .getPayload();
            if (null == ret) {
                return null;
            }
            return ret.toByteArray();
        } catch (InvalidArgumentException e) {
            throw e;
        } catch (Exception e) {
            throw new InvalidArgumentException(e);
        }
    }

    /**
     * getChaincodeActionResponseStatus returns the what chaincode executions set as the return status.
     *
     * @return status code.
     * @throws InvalidArgumentException
     */

    public int getChaincodeActionResponseStatus() throws InvalidArgumentException {

        if (statusReturnCode != -1) {
            return statusReturnCode;
        }

        try {

            final ProposalResponsePayloadDeserializer proposalResponsePayloadDeserializer = getProposalResponsePayloadDeserializer();
            statusReturnCode = proposalResponsePayloadDeserializer.getExtension().getResponseStatus();
            return statusReturnCode;

        } catch (InvalidArgumentException e) {
            throw e;
        } catch (Exception e) {
            throw new InvalidArgumentException(e);
        }

    }

    /**
     * getChaincodeActionResponseReadWriteSetInfo get this proposals read write set.
     *
     * @return The read write set. See {@link TxReadWriteSetInfo}
     * @throws InvalidArgumentException
     */

    public TxReadWriteSetInfo getChaincodeActionResponseReadWriteSetInfo() throws InvalidArgumentException {

        if (isInvalid()) {
            throw new InvalidArgumentException("Proposal response is invalid.");
        }

        try {

            final ProposalResponsePayloadDeserializer proposalResponsePayloadDeserializer = getProposalResponsePayloadDeserializer();

            TxReadWriteSet txReadWriteSet = proposalResponsePayloadDeserializer.getExtension().getResults();

            if (txReadWriteSet == null) {
                return null;
            }

            return new TxReadWriteSetInfo(txReadWriteSet);

        } catch (Exception e) {
            throw new InvalidArgumentException(e);
        }

    }

}