All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hyperledger.fabric.shim.impl.Handler Maven / Gradle / Ivy

There is a newer version: 2.5.3
Show newest version
/*
Copyright IBM Corp., DTCC All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package org.hyperledger.fabric.shim.impl;

import static java.lang.String.format;
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.COMPLETED;
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.DEL_STATE;
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.ERROR;
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.GET_PRIVATE_DATA_HASH;
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.GET_QUERY_RESULT;
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.GET_STATE;
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.GET_STATE_BY_RANGE;
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.GET_STATE_METADATA;
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.INVOKE_CHAINCODE;
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.KEEPALIVE;
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.PUT_STATE;
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.PUT_STATE_METADATA;
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.QUERY_STATE_CLOSE;
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.QUERY_STATE_NEXT;
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.READY;
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.REGISTER;
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.REGISTERED;
import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.RESPONSE;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import org.hyperledger.fabric.protos.peer.Chaincode.ChaincodeID;
import org.hyperledger.fabric.protos.peer.Chaincode.ChaincodeInput;
import org.hyperledger.fabric.protos.peer.Chaincode.ChaincodeSpec;
import org.hyperledger.fabric.protos.peer.ChaincodeEventPackage.ChaincodeEvent;
import org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage;
import org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type;
import org.hyperledger.fabric.protos.peer.ChaincodeShim.DelState;
import org.hyperledger.fabric.protos.peer.ChaincodeShim.GetQueryResult;
import org.hyperledger.fabric.protos.peer.ChaincodeShim.GetState;
import org.hyperledger.fabric.protos.peer.ChaincodeShim.GetStateByRange;
import org.hyperledger.fabric.protos.peer.ChaincodeShim.GetStateMetadata;
import org.hyperledger.fabric.protos.peer.ChaincodeShim.PutState;
import org.hyperledger.fabric.protos.peer.ChaincodeShim.PutStateMetadata;
import org.hyperledger.fabric.protos.peer.ChaincodeShim.QueryResponse;
import org.hyperledger.fabric.protos.peer.ChaincodeShim.QueryStateClose;
import org.hyperledger.fabric.protos.peer.ChaincodeShim.QueryStateNext;
import org.hyperledger.fabric.protos.peer.ChaincodeShim.StateMetadata;
import org.hyperledger.fabric.protos.peer.ChaincodeShim.StateMetadataResult;
import org.hyperledger.fabric.protos.peer.ProposalResponsePackage.Response;
import org.hyperledger.fabric.protos.peer.ProposalResponsePackage.Response.Builder;
import org.hyperledger.fabric.shim.Chaincode;
import org.hyperledger.fabric.shim.ChaincodeStub;
import org.hyperledger.fabric.shim.helper.Channel;

import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;

public class Handler {

    private static Logger logger = Logger.getLogger(Handler.class.getName());
    private final Chaincode chaincode;
    private final Map isTransaction = new HashMap<>();
    private final Map> responseChannel = new HashMap<>();
    private Channel outboundChaincodeMessages = new Channel<>();
    private CCState state;

    public Handler(ChaincodeID chaincodeId, Chaincode chaincode) {
        this.chaincode = chaincode;
        this.state = CCState.CREATED;
        queueOutboundChaincodeMessage(newRegisterChaincodeMessage(chaincodeId));
    }

    public ChaincodeMessage nextOutboundChaincodeMessage() {
        try {
            return outboundChaincodeMessages.take();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            if (logger.isLoggable(Level.WARNING)) {
                logger.warning("Unable to get next outbound ChaincodeMessage");
            }
            return newErrorEventMessage("UNKNOWN", "UNKNOWN", e);
        }
    }

    public void onChaincodeMessage(ChaincodeMessage chaincodeMessage) {
        if (logger.isLoggable(Level.FINE)) {
            logger.fine(format("[%-8.8s] %s", chaincodeMessage.getTxid(), toJsonString(chaincodeMessage)));
        }
        handleChaincodeMessage(chaincodeMessage);
    }

    private synchronized void handleChaincodeMessage(ChaincodeMessage message) {
        if (logger.isLoggable(Level.FINE)) {
            logger.fine(format("[%-8.8s] Handling ChaincodeMessage of type: %s, handler state %s", message.getTxid(), message.getType(), this.state));
        }
        if (message.getType() == KEEPALIVE) {
            if (logger.isLoggable(Level.FINE)) {
                logger.fine(format("[%-8.8s] Received KEEPALIVE: nothing to do", message.getTxid()));
            }
            return;
        }
        switch (this.state) {
            case CREATED:
                handleCreated(message);
                break;
            case ESTABLISHED:
                handleEstablished(message);
                break;
            case READY:
                handleReady(message);
                break;
            default:
                if (logger.isLoggable(Level.WARNING)) {
                    logger.warning(format("[%-8.8s] Received %s: cannot handle", message.getTxid(), message.getType()));
                }
                break;
        }
    }

    private void handleCreated(ChaincodeMessage message) {
        if (message.getType() == REGISTERED) {
            this.state = CCState.ESTABLISHED;
            if (logger.isLoggable(Level.FINE)) {
                logger.fine(format("[%-8.8s] Received REGISTERED: moving to established state", message.getTxid()));
            }
        } else {
            if (logger.isLoggable(Level.WARNING)) {
                logger.warning(format("[%-8.8s] Received %s: cannot handle", message.getTxid(), message.getType()));
            }
        }
    }

    private void handleEstablished(ChaincodeMessage message) {
        if (message.getType() == READY) {
            this.state = CCState.READY;
            if (logger.isLoggable(Level.FINE)) {
                logger.fine(format("[%-8.8s] Received READY: ready for invocations", message.getTxid()));
            }
        } else {
            if (logger.isLoggable(Level.WARNING)) {
                logger.warning(format("[%-8.8s] Received %s: cannot handle", message.getTxid(), message.getType()));
            }
        }
    }

    private void handleReady(ChaincodeMessage message) {
        switch (message.getType()) {
            case RESPONSE:
                if (logger.isLoggable(Level.FINE)) {
                    logger.fine(format("[%-8.8s] Received RESPONSE: publishing to channel", message.getTxid()));
                }
                sendChannel(message);
                break;
            case ERROR:
                if (logger.isLoggable(Level.FINE)) {
                    logger.fine(format("[%-8.8s] Received ERROR: publishing to channel", message.getTxid()));
                }
                sendChannel(message);
                break;
            case INIT:
                if (logger.isLoggable(Level.FINE)) {
                    logger.fine(format("[%-8.8s] Received INIT: invoking chaincode init", message.getTxid()));
                }
                handleInit(message);
                break;
            case TRANSACTION:
                if (logger.isLoggable(Level.FINE)) {
                    logger.fine(format("[%-8.8s] Received TRANSACTION: invoking chaincode", message.getTxid()));
                }
                handleTransaction(message);
                break;
            default:
                if (logger.isLoggable(Level.WARNING)) {
                    logger.warning(format("[%-8.8s] Received %s: cannot handle", message.getTxid(), message.getType()));
                }
                break;
        }
    }

    private String getTxKey(final String channelId, final String txid) {
        return channelId + txid;
    }

    private void queueOutboundChaincodeMessage(ChaincodeMessage chaincodeMessage) {
        this.outboundChaincodeMessages.add(chaincodeMessage);
    }

    private synchronized Channel aquireResponseChannelForTx(final String channelId, final String txId) {
        final Channel channel = new Channel<>();
        String key = getTxKey(channelId, txId);
        if (this.responseChannel.putIfAbsent(key, channel) != null) {
            throw new IllegalStateException(format("[%-8.8s] Response channel already exists. Another request must be pending.", txId));
        }
        if (logger.isLoggable(Level.FINEST)) {
            logger.finest(format("[%-8.8s] Response channel created.", txId));
        }
        return channel;
    }

    private synchronized void sendChannel(ChaincodeMessage message) {
        String key = getTxKey(message.getChannelId(), message.getTxid());
        if (!responseChannel.containsKey(key)) {
            throw new IllegalStateException(format("[%-8.8s] sendChannel does not exist", message.getTxid()));
        }
        responseChannel.get(key).add(message);
    }

    private ChaincodeMessage receiveChannel(Channel channel) {
        try {
            return channel.take();
        } catch (InterruptedException e) {
            if (logger.isLoggable(Level.FINE)) {
                logger.fine("channel.take() failed with InterruptedException");
            }

            // Channel has been closed?
            // TODO
            return null;
        }
    }

    private synchronized void releaseResponseChannelForTx(String channelId, String txId) {
        String key = getTxKey(channelId, txId);
        final Channel channel = responseChannel.remove(key);
        if (channel != null) channel.close();
        if (logger.isLoggable(Level.FINER)) {
            logger.finer(format("[%-8.8s] Response channel closed.", txId));
        }
    }

    /**
     * Marks a CHANNELID+UUID as either a transaction or a query
     *
     * @param uuid          ID to be marked
     * @param isTransaction true for transaction, false for query
     * @return whether or not the UUID was successfully marked
     */
    private synchronized boolean markIsTransaction(String channelId, String uuid, boolean isTransaction) {
        if (this.isTransaction == null) {
            return false;
        }

        String key = getTxKey(channelId, uuid);
        this.isTransaction.put(key, isTransaction);
        return true;
    }

    private synchronized void deleteIsTransaction(String channelId, String uuid) {
        String key = getTxKey(channelId, uuid);
        isTransaction.remove(key);
    }

    /**
     * Handles requests to initialize chaincode
     *
     * @param message chaincode to be initialized
     */
    private void handleInit(ChaincodeMessage message) {
        new Thread(() -> {
            try {

                // Get the function and args from Payload
                final ChaincodeInput input = ChaincodeInput.parseFrom(message.getPayload());

                // Mark as a transaction (allow put/del state)
                markIsTransaction(message.getChannelId(), message.getTxid(), true);

                // Create the ChaincodeStub which the chaincode can use to
                // callback
                final ChaincodeStub stub = new ChaincodeStubImpl(message.getChannelId(), message.getTxid(), this, input.getArgsList(), message.getProposal());

                // Call chaincode's init
                final Chaincode.Response result = chaincode.init(stub);

                if (result.getStatus().getCode() >= Chaincode.Response.Status.INTERNAL_SERVER_ERROR.getCode()) {
                    // Send ERROR with entire result.Message as payload
                    logger.severe(format("[%-8.8s] Init failed. Sending %s", message.getTxid(), ERROR));
                    queueOutboundChaincodeMessage(newErrorEventMessage(message.getChannelId(), message.getTxid(), result.getMessage(), stub.getEvent()));
                } else {
                    // Send COMPLETED with entire result as payload
                    if (logger.isLoggable(Level.FINE)) {
                        logger.fine(format(format("[%-8.8s] Init succeeded. Sending %s", message.getTxid(), COMPLETED)));
                    }
                    queueOutboundChaincodeMessage(newCompletedEventMessage(message.getChannelId(), message.getTxid(), result, stub.getEvent()));
                }
            } catch (InvalidProtocolBufferException | RuntimeException e) {
                logger.severe(format("[%-8.8s] Init failed. Sending %s: %s", message.getTxid(), ERROR, e));
                queueOutboundChaincodeMessage(newErrorEventMessage(message.getChannelId(), message.getTxid(), e));
            } finally {
                // delete isTransaction entry
                deleteIsTransaction(message.getChannelId(), message.getTxid());
            }
        }).start();
    }

    // handleTransaction Handles request to execute a transaction.
    private void handleTransaction(ChaincodeMessage message) {
        new Thread(() -> {
            try {

                // Get the function and args from Payload
                final ChaincodeInput input = ChaincodeInput.parseFrom(message.getPayload());

                // Mark as a transaction (allow put/del state)
                markIsTransaction(message.getChannelId(), message.getTxid(), true);

                // Create the ChaincodeStub which the chaincode can use to
                // callback
                final ChaincodeStub stub = new ChaincodeStubImpl(message.getChannelId(), message.getTxid(), this, input.getArgsList(), message.getProposal());

                // Call chaincode's invoke
                final Chaincode.Response result = chaincode.invoke(stub);

                if (result.getStatus().getCode() >= Chaincode.Response.Status.INTERNAL_SERVER_ERROR.getCode()) {
                    // Send ERROR with entire result.Message as payload
                    logger.severe(format("[%-8.8s] Invoke failed. Sending %s", message.getTxid(), ERROR));
                    queueOutboundChaincodeMessage(newErrorEventMessage(message.getChannelId(), message.getTxid(), result.getMessage(), stub.getEvent()));
                } else {
                    // Send COMPLETED with entire result as payload
                    if (logger.isLoggable(Level.FINE)) {
                        logger.fine(format(format("[%-8.8s] Invoke succeeded. Sending %s", message.getTxid(), COMPLETED)));
                    }
                    queueOutboundChaincodeMessage(newCompletedEventMessage(message.getChannelId(), message.getTxid(), result, stub.getEvent()));
                }

            } catch (InvalidProtocolBufferException | RuntimeException e) {
                logger.severe(format("[%-8.8s] Invoke failed. Sending %s: %s", message.getTxid(), ERROR, e));
                queueOutboundChaincodeMessage(newErrorEventMessage(message.getChannelId(), message.getTxid(), e));
            } finally {
                // delete isTransaction entry
                deleteIsTransaction(message.getChannelId(), message.getTxid());
            }
        }).start();
    }

    // handleGetState communicates with the validator to fetch the requested state information from the ledger.
    ByteString getState(String channelId, String txId, String collection, String key) {
        return invokeChaincodeSupport(newGetStateEventMessage(channelId, txId, collection, key));
    }

    ByteString getPrivateDataHash(String channelId, String txId, String collection, String key) {
        return invokeChaincodeSupport(newGetPrivateDataHashEventMessage(channelId, txId, collection, key));
    }

    Map getStateMetadata(String channelId, String txId, String collection, String key) {
        ByteString payload = invokeChaincodeSupport(newGetStateMetadataEventMessage(channelId, txId, collection, key));
        try {
            StateMetadataResult stateMetadataResult = StateMetadataResult.parseFrom(payload);
            Map stateMetadataMap = new HashMap<>();
            stateMetadataResult.getEntriesList().forEach(entry -> stateMetadataMap.put(entry.getMetakey(), entry.getValue()));
            return stateMetadataMap;
        } catch (InvalidProtocolBufferException e) {
            logger.severe(String.format("[%-8.8s] unmarshall error", txId));
            throw new RuntimeException("Error unmarshalling StateMetadataResult.", e);
        }
    }

    private boolean isTransaction(String channelId, String uuid) {
        String key = getTxKey(channelId, uuid);
        return isTransaction.containsKey(key) && isTransaction.get(key);
    }

    void putState(String channelId, String txId, String collection, String key, ByteString value) {
        if (logger.isLoggable(Level.FINE)) {
            logger.fine(format("[%-8.8s] Inside putstate (\"%s\":\"%s\":\"%s\"), isTransaction = %s", txId, collection, key, value, isTransaction(channelId, txId)));
        }
        if (!isTransaction(channelId, txId)) throw new IllegalStateException("Cannot put state in query context");
        invokeChaincodeSupport(newPutStateEventMessage(channelId, txId, collection, key, value));
    }

    void putStateMetadata(String channelId, String txId, String collection, String key, String metakey, ByteString value) {
        if (!isTransaction(channelId, txId)) {
            throw new IllegalStateException("Cannot put state metadata in query context");
        }
        invokeChaincodeSupport(newPutStateMatadateEventMessage(channelId, txId, collection, key, metakey, value));
    }

    void deleteState(String channelId, String txId, String collection, String key) {
        if (!isTransaction(channelId, txId)) throw new RuntimeException("Cannot del state in query context");
        invokeChaincodeSupport(newDeleteStateEventMessage(channelId, txId, collection, key));
    }

    QueryResponse getStateByRange(String channelId, String txId, String collection, String startKey, String endKey, ByteString metadata) {
        GetStateByRange.Builder msgBuilder = GetStateByRange.newBuilder()
            .setCollection(collection)
            .setStartKey(startKey)
            .setEndKey(endKey);
        if (metadata != null) {
            msgBuilder.setMetadata(metadata);
        }
        return invokeQueryResponseMessage(channelId, txId, GET_STATE_BY_RANGE, msgBuilder.build().toByteString());
    }

    QueryResponse queryStateNext(String channelId, String txId, String queryId) {
        return invokeQueryResponseMessage(channelId, txId, QUERY_STATE_NEXT, QueryStateNext.newBuilder()
                .setId(queryId)
                .build().toByteString());
    }

    void queryStateClose(String channelId, String txId, String queryId) {
        invokeQueryResponseMessage(channelId, txId, QUERY_STATE_CLOSE, QueryStateClose.newBuilder()
                .setId(queryId)
                .build().toByteString());
    }

    QueryResponse getQueryResult(String channelId, String txId, String collection, String query, ByteString metadata) {
        GetQueryResult.Builder msgBuilder = GetQueryResult.newBuilder()
                .setCollection(collection)
                .setQuery(query);
        if (metadata != null) {
            msgBuilder.setMetadata(metadata);
        }
        return invokeQueryResponseMessage(channelId, txId, GET_QUERY_RESULT, msgBuilder.build().toByteString());
    }

    QueryResponse getHistoryForKey(String channelId, String txId, String key) {
        return invokeQueryResponseMessage(channelId, txId, Type.GET_HISTORY_FOR_KEY, GetQueryResult.newBuilder()
                .setQuery(key)
                .build().toByteString());
    }

    private QueryResponse invokeQueryResponseMessage(String channelId, String txId, ChaincodeMessage.Type type, ByteString payload) {
        try {
            return QueryResponse.parseFrom(invokeChaincodeSupport(newEventMessage(type, channelId, txId, payload)));
        } catch (InvalidProtocolBufferException e) {
            logger.severe(String.format("[%-8.8s] unmarshall error", txId));
            throw new RuntimeException("Error unmarshalling QueryResponse.", e);
        }
    }

    private ByteString invokeChaincodeSupport(final ChaincodeMessage message) {
        final String channelId = message.getChannelId();
        final String txId = message.getTxid();

        try {
            // create a new response channel
            Channel responseChannel = aquireResponseChannelForTx(channelId, txId);

            // send the message
            queueOutboundChaincodeMessage(message);

            // wait for response
            final ChaincodeMessage response = receiveChannel(responseChannel);
            if (logger.isLoggable(Level.FINE)) {
                logger.fine(format("[%-8.8s] %s response received.", txId, response.getType()));
            }

            // handle response
            switch (response.getType()) {
                case RESPONSE:
                    if (logger.isLoggable(Level.FINE)) {
                        logger.fine(format("[%-8.8s] Successful response received.", txId));
                    }
                    return response.getPayload();
                case ERROR:
                    logger.severe(format("[%-8.8s] Unsuccessful response received.", txId));
                    throw new RuntimeException(format("[%-8.8s]Unsuccessful response received.", txId));
                default:
                    logger.severe(format("[%-8.8s] Unexpected %s response received. Expected %s or %s.", txId, response.getType(), RESPONSE, ERROR));
                    throw new RuntimeException(format("[%-8.8s]Unexpected %s response received. Expected %s or %s.", txId, response.getType(), RESPONSE, ERROR));
            }
        } finally {
            releaseResponseChannelForTx(channelId, txId);
        }
    }

    Chaincode.Response invokeChaincode(String channelId, String txId, String chaincodeName, List args) {
        try {
            // create invocation specification of the chaincode to invoke
            final ChaincodeSpec invocationSpec = ChaincodeSpec.newBuilder()
                    .setChaincodeId(ChaincodeID.newBuilder()
                            .setName(chaincodeName)
                            .build())
                    .setInput(ChaincodeInput.newBuilder()
                            .addAllArgs(args.stream().map(ByteString::copyFrom).collect(Collectors.toList()))
                            .build())
                    .build();

            // invoke other chaincode
            final ByteString payload = invokeChaincodeSupport(newInvokeChaincodeMessage(channelId, txId, invocationSpec.toByteString()));

            // response message payload should be yet another chaincode
            // message (the actual response message)
            final ChaincodeMessage responseMessage = ChaincodeMessage.parseFrom(payload);
            // the actual response message must be of type COMPLETED
            if (logger.isLoggable(Level.FINE)) {
                logger.fine(format("[%-8.8s] %s response received from other chaincode.", txId, responseMessage.getType()));
            }
            if (responseMessage.getType() == COMPLETED) {
                // success
                return toChaincodeResponse(Response.parseFrom(responseMessage.getPayload()));
            } else {
                // error
                return newErrorChaincodeResponse(responseMessage.getPayload().toStringUtf8());
            }
        } catch (InvalidProtocolBufferException e) {
            throw new RuntimeException(e);
        }
    }

    private static String toJsonString(ChaincodeMessage message) {
        try {
            return JsonFormat.printer().print(message);
        } catch (InvalidProtocolBufferException e) {
            return String.format("{ Type: %s, TxId: %s }", message.getType(), message.getTxid());
        }
    }

    private static Chaincode.Response newErrorChaincodeResponse(String message) {
        return new Chaincode.Response(Chaincode.Response.Status.INTERNAL_SERVER_ERROR, message, null);
    }

    private static ChaincodeMessage newGetPrivateDataHashEventMessage(final String channelId, final String txId, final String collection, final String key) {
        return newEventMessage(GET_PRIVATE_DATA_HASH, channelId, txId, GetState.newBuilder()
                .setCollection(collection)
                .setKey(key)
                .build().toByteString());
    }

    private static ChaincodeMessage newGetStateEventMessage(final String channelId, final String txId, final String collection, final String key) {
        return newEventMessage(GET_STATE, channelId, txId, GetState.newBuilder()
                .setCollection(collection)
                .setKey(key)
                .build().toByteString());
    }

    private static ChaincodeMessage newGetStateMetadataEventMessage(final String channelId, final String txId, final String collection, final String key) {
        return newEventMessage(GET_STATE_METADATA, channelId, txId,
                GetStateMetadata.newBuilder()
                .setCollection(collection)
                .setKey(key)
                .build().toByteString());
    }

    private static ChaincodeMessage newPutStateEventMessage(final String channelId, final String txId, final String collection, final String key, final ByteString value) {
        return newEventMessage(PUT_STATE, channelId, txId, PutState.newBuilder()
                .setCollection(collection)
                .setKey(key)
                .setValue(value)
                .build().toByteString());
    }

    private static ChaincodeMessage newPutStateMatadateEventMessage(final String channelId, final String txId, final String collection, final String key, final String metakey, final ByteString value) {
        return newEventMessage(PUT_STATE_METADATA, channelId, txId,
                PutStateMetadata.newBuilder()
                        .setCollection(collection)
                        .setKey(key)
                        .setMetadata(StateMetadata.newBuilder()
                                .setMetakey(metakey)
                                .setValue(value)
                                .build())
                        .build().toByteString());
    }

    private static ChaincodeMessage newDeleteStateEventMessage(final String channelId, final String txId, final String collection, final String key) {
        return newEventMessage(DEL_STATE, channelId, txId, DelState.newBuilder()
                .setCollection(collection)
                .setKey(key)
                .build().toByteString());
    }

    private static ChaincodeMessage newErrorEventMessage(final String channelId, final String txId, final Throwable throwable) {
        return newErrorEventMessage(channelId, txId, printStackTrace(throwable));
    }

    private static ChaincodeMessage newErrorEventMessage(final String channelId, final String txId, final String message) {
        return newErrorEventMessage(channelId, txId, message, null);
    }

    private static ChaincodeMessage newErrorEventMessage(final String channelId, final String txId, final String message, final ChaincodeEvent event) {
        return newEventMessage(ERROR, channelId, txId, ByteString.copyFromUtf8(message), event);
    }

    private static ChaincodeMessage newCompletedEventMessage(final String channelId, final String txId, final Chaincode.Response response, final ChaincodeEvent event) {
        ChaincodeMessage message = newEventMessage(COMPLETED, channelId, txId, toProtoResponse(response).toByteString(), event);
        return message;
    }

    private static ChaincodeMessage newInvokeChaincodeMessage(final String channelId, final String txId, final ByteString payload) {
        return newEventMessage(INVOKE_CHAINCODE, channelId, txId, payload, null);
    }

    private static ChaincodeMessage newRegisterChaincodeMessage(final ChaincodeID chaincodeId) {
        return ChaincodeMessage.newBuilder()
                .setType(REGISTER)
                .setPayload(chaincodeId.toByteString())
                .build();
    }

    private static ChaincodeMessage newEventMessage(final Type type, final String channelId, final String txId, final ByteString payload) {
        return newEventMessage(type, channelId, txId, payload, null);
    }

    private static ChaincodeMessage newEventMessage(final Type type, final String channelId, final String txId, final ByteString payload, final ChaincodeEvent event) {
        ChaincodeMessage.Builder builder = ChaincodeMessage.newBuilder()
                .setType(type)
                .setChannelId(channelId)
                .setTxid(txId)
                .setPayload(payload);
        if (event != null) {
            builder.setChaincodeEvent(event);
        }
        return builder.build();
    }

    private static Response toProtoResponse(Chaincode.Response response) {
        final Builder builder = Response.newBuilder();
        builder.setStatus(response.getStatus().getCode());
        if (response.getMessage() != null) {
            builder.setMessage(response.getMessage());
        }
        if (response.getPayload() != null) {
            builder.setPayload(ByteString.copyFrom(response.getPayload()));
        }
        return builder.build();
    }

    private static Chaincode.Response toChaincodeResponse(Response response) {
        return new Chaincode.Response(
                Chaincode.Response.Status.forCode(response.getStatus()),
                response.getMessage(),
                response.getPayload() == null ? null : response.getPayload().toByteArray()
        );
    }

    private static String printStackTrace(Throwable throwable) {
        if (throwable == null) return null;
        final StringWriter buffer = new StringWriter();
        throwable.printStackTrace(new PrintWriter(buffer));
        return buffer.toString();
    }

    public enum CCState {
        CREATED,
        ESTABLISHED,
        READY
    }

    CCState getState() {
        return this.state;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy