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

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

There is a newer version: 1.1
Show newest version
/*
Copyright DTCC 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

         http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package org.hyperledger.fabric.sdk.shim;

import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import io.grpc.stub.StreamObserver;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperledger.fabric.sdk.exception.InvalidTransactionException;
import org.hyperledger.fabric.sdk.helper.Channel;
import org.hyperledger.fabric.sdk.shim.fsm.*;
import org.hyperledger.fabric.sdk.shim.fsm.exceptions.CancelledException;
import org.hyperledger.fabric.sdk.shim.fsm.exceptions.NoTransitionException;
import org.hyperledger.protos.Chaincode.*;
import org.hyperledger.protos.Chaincode.ChaincodeMessage.Builder;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.hyperledger.protos.Chaincode.ChaincodeMessage.Type.*;

public class Handler {

	private static Log logger = LogFactory.getLog(Handler.class);

	private StreamObserver chatStream;
	private ChaincodeBase chaincode;

	private Map isTransaction;
	private Map> responseChannel;
	public Channel nextState;

	private FSM fsm;

	public Handler(StreamObserver chatStream, ChaincodeBase chaincode) {
		this.chatStream = chatStream;
		this.chaincode = chaincode;

		responseChannel = new HashMap>();
		isTransaction = new HashMap();
		nextState = new Channel();

		fsm = new FSM("created");

		fsm.addEvents(
				//				Event Name				Destination		Sources States
				new EventDesc(REGISTERED.toString(), 	"established",	"created"),
				new EventDesc(INIT.toString(), 			"init", 		"established"),
				new EventDesc(READY.toString(), 		"ready", 		"established"),
				new EventDesc(ERROR.toString(), 		"established", 	"init"),
				new EventDesc(RESPONSE.toString(),		"init", 		"init"),
				new EventDesc(COMPLETED.toString(), 	"ready", 		"init"),
				new EventDesc(TRANSACTION.toString(),	"transaction", 	"ready"),
				new EventDesc(COMPLETED.toString(), 	"ready", 		"transaction"),
				new EventDesc(ERROR.toString(), 		"ready", 		"transaction"),
				new EventDesc(RESPONSE.toString(), 		"transaction", 	"transaction"),
				new EventDesc(QUERY.toString(), 		"transaction", 	"transaction"),
				new EventDesc(QUERY.toString(), 		"ready", 		"ready"),
				new EventDesc(RESPONSE.toString(), 		"ready", 		"ready")
				);

		fsm.addCallbacks(
				//			Type			Trigger					Callback
				new CBDesc(CallbackType.BEFORE_EVENT,	REGISTERED.toString(), 	(event) -> beforeRegistered(event)),
				new CBDesc(CallbackType.AFTER_EVENT, 	RESPONSE.toString(), 	(event) -> afterResponse(event)),
				new CBDesc(CallbackType.AFTER_EVENT, 	ERROR.toString(), 		(event) -> afterError(event)),
				new CBDesc(CallbackType.ENTER_STATE, 	"init", 				(event) -> enterInitState(event)),
				new CBDesc(CallbackType.ENTER_STATE, 	"transaction", 			(event) -> enterTransactionState(event)),
				new CBDesc(CallbackType.BEFORE_EVENT, 	QUERY.toString(), 		(event) -> beforeQuery(event))
				);
	}

	public static String shortID(String uuid) {
		if (uuid.length() < 8) {
			return uuid;
		} else {
			return uuid.substring(0, 8);
		}
	}

	public void triggerNextState(ChaincodeMessage message, boolean send) {
		if(logger.isTraceEnabled())logger.trace("triggerNextState for message "+message);
		nextState.add(new NextStateInfo(message, send));
	}

	public synchronized void serialSend(ChaincodeMessage message) {
		try {
			chatStream.onNext(message);
		} catch (Exception e) {
			logger.error(String.format("[%s]Error sending %s: %s",
					shortID(message), message.getType(), e));
			throw new RuntimeException(String.format("Error sending %s: %s", message.getType(), e));
		}
		if(logger.isTraceEnabled()) {
            logger.trace("serialSend complete for message "+message);
        }
	}

	public synchronized Channel createChannel(String uuid) {
		if (responseChannel.containsKey(uuid)) {
			throw new IllegalStateException("[" + shortID(uuid) + "] Channel exists");
		}

		Channel channel = new Channel();
		responseChannel.put(uuid, channel);
		if(logger.isTraceEnabled()){
            logger.trace("channel created with uuid "+uuid);
        }

		return channel;
	}

	public synchronized void sendChannel(ChaincodeMessage message) {
		if (!responseChannel.containsKey(message.getTxid())) {
			throw new IllegalStateException("[" + shortID(message) + "]sendChannel does not exist");
		}

		logger.debug(String.format("[%s]Before send", shortID(message)));
		responseChannel.get(message.getTxid()).add(message);
		logger.debug(String.format("[%s]After send", shortID(message)));
	}

	public ChaincodeMessage receiveChannel(Channel channel) {
		try {
			return channel.take();
		} catch (InterruptedException e) {
			logger.debug("channel.take() failed with InterruptedException");

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

	public synchronized void deleteChannel(String uuid) {
		Channel channel = responseChannel.remove(uuid);
		if (channel != null) {
			channel.close();
		}

		if(logger.isTraceEnabled()){
            logger.trace("deleteChannel done with uuid "+uuid);
        }
	}

	/**
	 * Marks a 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
	 */
	public synchronized boolean markIsTransaction(String uuid, boolean isTransaction) {
		if (this.isTransaction == null) {
			return false;
		}

		this.isTransaction.put(uuid, isTransaction);
		return true;
	}

	public synchronized void deleteIsTransaction(String uuid) {
		isTransaction.remove(uuid);
	}

	public void beforeRegistered(Event event) {
		messageHelper(event);
		logger.debug(String.format("Received %s, ready for invocations", REGISTERED));
	}

	/**
	 * Handles requests to initialize chaincode
	 * @param message chaincode to be initialized
	 */
	public void handleInit(ChaincodeMessage message) throws InvalidTransactionException  {
		Runnable task = () -> {
			ChaincodeMessage nextStatemessage = null;
			boolean send = true;
			try {
				// Get the function and args from Payload
				ChaincodeInput input;
				try {
					input = ChaincodeInput.parseFrom(message.getPayload());
				} catch (InvalidProtocolBufferException e) {
                    nextStatemessage = ChaincodeMessage.newBuilder()
                            .setType(ERROR)
                            .setPayload(ByteString.copyFromUtf8("Unmarshall error -"
                                    + e.getMessage()))
                            .setTxid(message.getTxid())
                            .build();
                    return;
				}

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

				// Create the ChaincodeStub which the chaincode can use to callback
				ChaincodeStub stub = new ChaincodeStub(message.getTxid(), this, message.getSecurityContext());
				// Call chaincode's Run
				ByteString result;
				try {
					result = chaincode.runHelper(stub, getFunction(input.getArgsList()), getParameters(input.getArgsList()));
				} catch (Exception e) {
					// Send ERROR message to chaincode support and change state
					logger.debug(String.format("[%s]Init failed. Sending %s", shortID(message), ERROR));
					nextStatemessage = ChaincodeMessage.newBuilder()
							.setType(ERROR)
							.setPayload(ByteString.copyFromUtf8(e.getMessage()))
							.setTxid(message.getTxid())
							.build();
					return;
				} finally {
					// delete isTransaction entry
					deleteIsTransaction(message.getTxid());
				}

				// Send COMPLETED message to chaincode support and change state
				nextStatemessage = ChaincodeMessage.newBuilder()
						.setType(COMPLETED)
						.setPayload(result)
						.setTxid(message.getTxid())
						.build();

				logger.debug(String.format(String.format("[%s]Init succeeded. Sending %s",
						shortID(message), COMPLETED)));

				//TODO put in all exception states
			}
		 finally {
				triggerNextState(nextStatemessage, send);
			}
		};

		//Run above task
		new Thread(task).start();
	}

	private String getFunction(List args) {
		return (args.size() > 0) ? args.get(0).toStringUtf8() : "";
	}

	private String[] getParameters(List args) {
		int size = (args.size() == 0) ? 0 : args.size() - 1;
		String[] strArgs = new String[size];
		for(int i = 1; i < args.size(); ++i) {
			strArgs[i-1] = args.get(i).toStringUtf8();
		}
		return strArgs;
	}

	// enterInitState will initialize the chaincode if entering init from established.
	public void enterInitState(Event event) {
		logger.debug(String.format("Entered state %s", fsm.current()));
		ChaincodeMessage message = messageHelper(event);
		logger.debug(String.format("[%s]Received %s, initializing chaincode",
				shortID(message), message.getType().toString()));
		if (message.getType() == INIT) {
			// Call the chaincode's Run function to initialize
            try {
                handleInit(message);
            } catch (InvalidTransactionException e) {
                e.printStackTrace();
            }
        }
	}

	//
	// handleTransaction Handles request to execute a transaction.
	public void handleTransaction(ChaincodeMessage message) {
		// The defer followed by triggering a go routine dance is needed to ensure that the previous state transition
		// is completed before the next one is triggered. The previous state transition is deemed complete only when
		// the beforeInit function is exited. Interesting bug fix!!
		Runnable task = () -> {
			//better not be nil
			ChaincodeMessage nextStatemessage = null;
			boolean send = true;

			//Defer
			try {
				// Get the function and args from Payload
				ChaincodeInput input;
				try {
					input = ChaincodeInput.parseFrom(message.getPayload());
				} catch (Exception e) {
					logger.debug(String.format("[%s]Incorrect payload format. Sending %s", shortID(message), ERROR));
					// Send ERROR message to chaincode support and change state
					nextStatemessage = ChaincodeMessage.newBuilder()
							.setType(ERROR)
							.setPayload(message.getPayload())
							.setTxid(message.getTxid())
							.build();
					return;
				}

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

				// Create the ChaincodeStub which the chaincode can use to callback
				ChaincodeStub stub = new ChaincodeStub(message.getTxid(), this, message.getSecurityContext());

				// Call chaincode's Run
				ByteString response;
				try {
					response = chaincode.runHelper(stub, getFunction(input.getArgsList()), getParameters(input.getArgsList()));
				} catch (Exception e) {
					e.printStackTrace();
					System.err.flush();
					// Send ERROR message to chaincode support and change state
					logger.error(String.format("[%s]Error running chaincode. Transaction execution failed. Sending %s",
							shortID(message), ERROR));
					nextStatemessage = ChaincodeMessage.newBuilder()
							.setType(ERROR)
							.setPayload(message.getPayload())
							.setTxid(message.getTxid())
							.build();
					return;
				} finally {
					deleteIsTransaction(message.getTxid());
				}

				logger.debug(String.format("[%s]Transaction completed. Sending %s",
						shortID(message), COMPLETED));

				// Send COMPLETED message to chaincode support and change state
				Builder builder = ChaincodeMessage.newBuilder()
						.setType(COMPLETED)
						.setTxid(message.getTxid());
				if (response != null) builder.setPayload(response);
				nextStatemessage = builder.build();
			} finally {
				triggerNextState(nextStatemessage, send);
			}
		};

		new Thread(task).start();
	}

	// handleQuery handles request to execute a query.
	public void handleQuery(ChaincodeMessage message) {
		// Query does not transition state. It can happen anytime after Ready
		Runnable task = () -> {
			ChaincodeMessage serialSendMessage = null;
			try {
				// Get the function and args from Payload
				ChaincodeInput input;
				try {
					input = ChaincodeInput.parseFrom(message.getPayload());
				} catch (Exception e) {
					// Send ERROR message to chaincode support and change state
					logger.debug(String.format("[%s]Incorrect payload format. Sending %s",
							shortID(message), QUERY_ERROR));
					serialSendMessage = ChaincodeMessage.newBuilder()
							.setType(QUERY_ERROR)
							.setPayload(ByteString.copyFromUtf8(e.getMessage()))
							.setTxid(message.getTxid())
							.build();
					return;
				}

				// Mark as a query (do not allow put/del state)
				markIsTransaction(message.getTxid(), false);

				// Call chaincode's Query
				// Create the ChaincodeStub which the chaincode can use to callback
				ChaincodeStub stub = new ChaincodeStub(message.getTxid(), this, message.getSecurityContext());
				ByteString response;
				try {
					response = chaincode.queryHelper(stub, getFunction(input.getArgsList()), getParameters(input.getArgsList()));
				} catch (Exception e) {
					// Send ERROR message to chaincode support and change state
					logger.debug(String.format("[%s]Query execution failed. Sending %s",
							shortID(message), QUERY_ERROR));
					serialSendMessage = ChaincodeMessage.newBuilder()
							.setType(QUERY_ERROR)
							.setPayload(ByteString.copyFromUtf8(e.getMessage()))
							.setTxid(message.getTxid())
							.build();
					return;
				} finally {
					deleteIsTransaction(message.getTxid());
				}

				// Send COMPLETED message to chaincode support
				logger.debug("["+ shortID(message)+"]Query completed. Sending "+ QUERY_COMPLETED);
				serialSendMessage = ChaincodeMessage.newBuilder()
						.setType(QUERY_COMPLETED)
						.setPayload(response)
						.setTxid(message.getTxid())
						.build();
			} finally {
				serialSend(serialSendMessage);
			}
		};

		new Thread(task).start();
	}

	// enterTransactionState will execute chaincode's Run if coming from a TRANSACTION event.
	public void enterTransactionState(Event event) {
		ChaincodeMessage message = messageHelper(event);
		logger.debug(String.format("[%s]Received %s, invoking transaction on chaincode(src:%s, dst:%s)",
				shortID(message), message.getType().toString(), event.src, event.dst));
		if (message.getType() == TRANSACTION) {
			// Call the chaincode's Run function to invoke transaction
			handleTransaction(message);
		}
	}

	// afterCompleted will need to handle COMPLETED event by sending message to the peer
	public void afterCompleted(Event event) {
		ChaincodeMessage message = messageHelper(event);
		logger.debug(String.format("[%s]sending COMPLETED to validator for tid", shortID(message)));
		try {
			serialSend(message);
		} catch (Exception e) {
			event.cancel(new Exception("send COMPLETED failed %s", e));
		}
	}

	// beforeQuery is invoked when a query message is received from the validator
	public void beforeQuery(Event event) {
		ChaincodeMessage message = messageHelper(event);
		handleQuery(message);
	}

	// afterResponse is called to deliver a response or error to the chaincode stub.
	public void afterResponse(Event event) {
		ChaincodeMessage message = messageHelper(event);
		try {
			sendChannel(message);
			logger.debug(String.format("[%s]Received %s, communicated (state:%s)",
					shortID(message), message.getType(), fsm.current()));
		} catch (Exception e) {
			logger.error(String.format("[%s]error sending %s (state:%s): %s", shortID(message),
					message.getType(), fsm.current(), e));
		}
	}

	private ChaincodeMessage messageHelper(Event event) {
		try {
			return (ChaincodeMessage) event.args[0];
		} catch (Exception e) {
			InvalidTransactionException error = new InvalidTransactionException("Received unexpected message type", e);
			event.cancel(error);
            throw e;
        }
	}

	public void afterError(Event event) {
		ChaincodeMessage message = messageHelper(event);
		/* TODO- revisit. This may no longer be needed with the serialized/streamlined messaging model
		 * There are two situations in which the ERROR event can be triggered:
		 * 1. When an error is encountered within handleInit or handleTransaction -
		 * some issue at the chaincode side; In this case there will be no responseChannel
		 * and the message has been sent to the validator.
		 * 2. The chaincode has initiated a request (get/put/del state) to the validator
		 * and is expecting a response on the responseChannel; If ERROR is received
		 * from validator, this needs to be notified on the responseChannel.
		 */
		try {
			sendChannel(message);
		} catch (Exception e) {
			logger.debug(String.format("[%s]Error received from validator %s, communicated(state:%s)",
					shortID(message), message.getType(), fsm.current()));
		}
	}

	// handleGetState communicates with the validator to fetch the requested state information from the ledger.
	public ByteString handleGetState(String key, String uuid) {
		try {
			//TODO Implement method to get and put entire state map and not one key at a time?
			// Create the channel on which to communicate the response from validating peer
			Channel responseChannel;
			try {
				responseChannel = createChannel(uuid);
			} catch (Exception e) {
				logger.debug("Another state request pending for this Uuid. Cannot process.");
				throw e;
			}

			// Send GET_STATE message to validator chaincode support
			ChaincodeMessage message = ChaincodeMessage.newBuilder()
					.setType(GET_STATE)
					.setPayload(ByteString.copyFromUtf8(key))
					.setTxid(uuid)
					.build();

			logger.debug(String.format("[%s]Sending %s", shortID(message), GET_STATE));
			try {
				serialSend(message);
			} catch (Exception e) {
				logger.error(String.format("[%s]error sending GET_STATE %s", shortID(uuid), e));
				throw new RuntimeException("could not send message");
			}

			// Wait on responseChannel for response
			ChaincodeMessage response;
			try {
				response = receiveChannel(responseChannel);
			} catch (Exception e) {
				logger.error(String.format("[%s]Received unexpected message type", shortID(uuid)));
				throw new RuntimeException("Received unexpected message type");
			}

			// Success response
			if (response.getType() == RESPONSE) {
				logger.debug(String.format("[%s]GetState received payload %s", shortID(response.getTxid()), RESPONSE));
				return response.getPayload();
			}

			// Error response
			if (response.getType() == ERROR) {
				logger.error(String.format("[%s]GetState received error %s", shortID(response.getTxid()), ERROR));
				throw new RuntimeException(response.getPayload().toString());
			}

			// Incorrect chaincode message received
			logger.error(String.format("[%s]Incorrect chaincode message %s received. Expecting %s or %s",
					shortID(response.getTxid()), response.getType(), RESPONSE, ERROR));
			throw new RuntimeException("Incorrect chaincode message received");
		} finally {
			deleteChannel(uuid);
		}
	}

	private boolean isTransaction(String uuid) {
		return isTransaction.containsKey(uuid) && isTransaction.get(uuid);
	}

	public void handlePutState(String key, ByteString value, String uuid) {
		// Check if this is a transaction
		logger.debug("["+ shortID(uuid)+"]Inside putstate (\""+key+"\":\""+value+"\"), isTransaction = "+isTransaction(uuid));

		if (!isTransaction(uuid)) {
			throw new IllegalStateException("Cannot put state in query context");
		}

		PutStateInfo payload = PutStateInfo.newBuilder()
				.setKey(key)
				.setValue(value)
				.build();

		// Create the channel on which to communicate the response from validating peer
		Channel responseChannel;
		try {
			responseChannel = createChannel(uuid);
		} catch (Exception e) {
			logger.error(String.format("[%s]Another state request pending for this Uuid. Cannot process.", shortID(uuid)));
			throw e;
		}

		//Defer
		try {
			// Send PUT_STATE message to validator chaincode support
			ChaincodeMessage message = ChaincodeMessage.newBuilder()
					.setType(PUT_STATE)
					.setPayload(payload.toByteString())
					.setTxid(uuid)
					.build();

			logger.debug(String.format("[%s]Sending %s", shortID(message), PUT_STATE));

			try {
				serialSend(message);
			} catch (Exception e) {
				logger.error(String.format("[%s]error sending PUT_STATE %s", message.getTxid(), e));
				throw new RuntimeException("could not send message");
			}

			// Wait on responseChannel for response
			ChaincodeMessage response;
			try {
				response = receiveChannel(responseChannel);
			} catch (Exception e) {
				//TODO figure out how to get uuid of receive channel
				logger.error(String.format("[%s]Received unexpected message type", e));
				throw e;
			}

			// Success response
			if (response.getType() == RESPONSE) {
				logger.debug(String.format("[%s]Received %s. Successfully updated state", shortID(response.getTxid()), RESPONSE));
				return;
			}

			// Error response
			if (response.getType() == ERROR) {
				logger.error(String.format("[%s]Received %s. Payload: %s", shortID(response.getTxid()), ERROR, response.getPayload()));
				throw new RuntimeException(response.getPayload().toStringUtf8());
			}

			// Incorrect chaincode message received
			logger.error(String.format("[%s]Incorrect chaincode message %s received. Expecting %s or %s",
					shortID(response.getTxid()), response.getType(), RESPONSE, ERROR));

			throw new RuntimeException("Incorrect chaincode message received");
		} catch (Exception e) {
			throw e;
		} finally {
			deleteChannel(uuid);
		}
	}

	public void handleDeleteState(String key, String uuid) {
		// Check if this is a transaction
		if (!isTransaction(uuid)) {
			throw new RuntimeException("Cannot del state in query context");
		}

		// Create the channel on which to communicate the response from validating peer
		Channel responseChannel;
		try {
			responseChannel = createChannel(uuid);
		} catch (Exception e) {
			logger.error(String.format("[%s]Another state request pending for this Uuid."
					+ " Cannot process create createChannel.", shortID(uuid)));
			throw e;
		}

		//Defer
		try {
			// Send DEL_STATE message to validator chaincode support
			ChaincodeMessage message = ChaincodeMessage.newBuilder()
					.setType(DEL_STATE)
					.setPayload(ByteString.copyFromUtf8(key))
					.setTxid(uuid)
					.build();
			logger.debug(String.format("[%s]Sending %s", shortID(uuid), DEL_STATE));
			try {
				serialSend(message);
			} catch (Exception e) {
				logger.error(String.format("[%s]error sending DEL_STATE %s", shortID(message), DEL_STATE));
				throw new RuntimeException("could not send message");
			}

			// Wait on responseChannel for response
			ChaincodeMessage response;
			try {
				response = receiveChannel(responseChannel);
			} catch (Exception e) {
				logger.error(String.format("[%s]Received unexpected message type", shortID(message)));
				throw new RuntimeException("Received unexpected message type");
			}

			if (response.getType() == RESPONSE) {
				// Success response
				logger.debug(String.format("[%s]Received %s. Successfully deleted state", message.getTxid(), RESPONSE));
				return;
			}

			if (response.getType() == ERROR) {
				// Error response
				logger.error(String.format("[%s]Received %s. Payload: %s", message.getTxid(), ERROR, response.getPayload()));
				throw new RuntimeException(response.getPayload().toStringUtf8());
			}

			// Incorrect chaincode message received
			logger.error(String.format("[%s]Incorrect chaincode message %s received. Expecting %s or %s",
					shortID(response.getTxid()), response.getType(), RESPONSE, ERROR));
			throw new RuntimeException("Incorrect chaincode message received");
		} finally {
			deleteChannel(uuid);
		}
	}

	public RangeQueryStateResponse handleRangeQueryState(String startKey, String endKey, String uuid) {
		// Create the channel on which to communicate the response from validating peer
		Channel responseChannel;
		try {
			responseChannel = createChannel(uuid);
		} catch (Exception e) {
			logger.debug(String.format("[%s]Another state request pending for this Uuid."
					+ " Cannot process.", shortID(uuid)));
			throw e;
		}

		//Defer
		try {
			// Send RANGE_QUERY_STATE message to validator chaincode support
			RangeQueryState payload = RangeQueryState.newBuilder()
					.setStartKey(startKey)
					.setEndKey(endKey)
					.build();

			ChaincodeMessage message = ChaincodeMessage.newBuilder()
					.setType(RANGE_QUERY_STATE)
					.setPayload(payload.toByteString())
					.setTxid(uuid)
					.build();

			logger.debug(String.format("[%s]Sending %s", shortID(message), RANGE_QUERY_STATE));
			try {
				serialSend(message);
			} catch (Exception e){
				logger.error(String.format("[%s]error sending %s", shortID(message), RANGE_QUERY_STATE));
				throw new RuntimeException("could not send message");
			}

			// Wait on responseChannel for response
			ChaincodeMessage response;
			try {
				response = receiveChannel(responseChannel);
			} catch (Exception e) {
				logger.error(String.format("[%s]Received unexpected message type", uuid));
				throw new RuntimeException("Received unexpected message type");
			}

			if (response.getType() == RESPONSE) {
				// Success response
				logger.debug(String.format("[%s]Received %s. Successfully got range",
						shortID(response.getTxid()), RESPONSE));

				RangeQueryStateResponse rangeQueryResponse;
				try {
					rangeQueryResponse = RangeQueryStateResponse.parseFrom(response.getPayload());
				} catch (Exception e) {
					logger.error(String.format("[%s]unmarshall error", shortID(response.getTxid())));
					throw new RuntimeException("Error unmarshalling RangeQueryStateResponse.");
				}

				return rangeQueryResponse;
			}

			if (response.getType() == ERROR) {
				// Error response
				logger.error(String.format("[%s]Received %s",
						shortID(response.getTxid()), ERROR));
				throw new RuntimeException(response.getPayload().toStringUtf8());
			}

			// Incorrect chaincode message received
			logger.error(String.format("Incorrect chaincode message %s recieved. Expecting %s or %s",
					response.getType(), RESPONSE, ERROR));
			throw new RuntimeException("Incorrect chaincode message received");
		} finally {
			deleteChannel(uuid);
		}
	}

	public ByteString handleInvokeChaincode(String chaincodeName, String function, List args, String uuid) {
		// Check if this is a transaction
		if (!isTransaction.containsKey(uuid)) {
			throw new RuntimeException("Cannot invoke chaincode in query context");
		}

		ChaincodeID id = ChaincodeID.newBuilder()
				.setName(chaincodeName).build();
		ChaincodeInput input = ChaincodeInput.newBuilder()
				.addArgs(ByteString.copyFromUtf8(function))
				.addAllArgs(args)
				.build();
		ChaincodeSpec payload = ChaincodeSpec.newBuilder()
				.setChaincodeID(id)
				.setCtorMsg(input)
				.build();

		// Create the channel on which to communicate the response from validating peer
		Channel responseChannel;
		try {
			responseChannel = createChannel(uuid);
		} catch (Exception e) {
			logger.error(String.format("[%s]Another state request pending for this Uuid. Cannot process.", shortID(uuid)));
			throw e;
		}

		//Defer
		try {
			// Send INVOKE_CHAINCODE message to validator chaincode support
			ChaincodeMessage message = ChaincodeMessage.newBuilder()
					.setType(INVOKE_CHAINCODE)
					.setPayload(payload.toByteString())
					.setTxid(uuid)
					.build();

			logger.debug(String.format("[%s]Sending %s",
					shortID(message), INVOKE_CHAINCODE));

			try {
				serialSend(message);
			} catch (Exception e) {
				logger.error("["+ shortID(message)+"]Error sending "+INVOKE_CHAINCODE+": "+e.getMessage());
				throw e;
			}

			// Wait on responseChannel for response
			ChaincodeMessage response;
			try {
				response = receiveChannel(responseChannel);
			} catch (Exception e) {
				logger.error(String.format("[%s]Received unexpected message type", shortID(message)));
				throw new RuntimeException("Received unexpected message type");
			}

			if (response.getType() == RESPONSE) {
				// Success response
				logger.debug(String.format("[%s]Received %s. Successfully invoked chaincode", shortID(response.getTxid()), RESPONSE));
				return response.getPayload();
			}

			if (response.getType() == ERROR) {
				// Error response
				logger.error(String.format("[%s]Received %s.", shortID(response.getTxid()), ERROR));
				throw new RuntimeException(response.getPayload().toStringUtf8());
			}

			// Incorrect chaincode message received
			logger.debug(String.format("[%s]Incorrect chaincode message %s received. Expecting %s or %s",
					shortID(response.getTxid()), response.getType(), RESPONSE, ERROR));
			throw new RuntimeException("Incorrect chaincode message received");
		} finally {
			deleteChannel(uuid);
		}
	}

	public ByteString handleQueryChaincode(String chaincodeName, String function, List args, String uuid) {
		ChaincodeID id = ChaincodeID.newBuilder().setName(chaincodeName).build();
		ChaincodeInput input = ChaincodeInput.newBuilder()
				.addArgs(ByteString.copyFromUtf8(function))
				.addAllArgs(args)
				.build();
		ChaincodeSpec payload = ChaincodeSpec.newBuilder()
				.setChaincodeID(id)
				.setCtorMsg(input)
				.build();

		// Create the channel on which to communicate the response from validating peer
		Channel responseChannel;
		try {
			responseChannel = createChannel(uuid);
		} catch (Exception e) {
			logger.debug(String.format("Another request pending for this Uuid. Cannot process."));
			throw e;
		}

		//Defer
		try {

			// Send INVOKE_QUERY message to validator chaincode support
			ChaincodeMessage message = ChaincodeMessage.newBuilder()
					.setType(INVOKE_QUERY)
					.setPayload(payload.toByteString())
					.setTxid(uuid)
					.build();

			logger.debug(String.format("[%s]Sending %s", shortID(message), INVOKE_QUERY));

			try {
				serialSend(message);
			} catch (Exception e) {
				logger.error(String.format("[%s]error sending %s", shortID(message), INVOKE_QUERY));
				throw new RuntimeException("could not send message");
			}

			// Wait on responseChannel for response
			ChaincodeMessage response;
			try {
				response = receiveChannel(responseChannel);
			} catch (Exception e) {
				logger.error(String.format("[%s]Received unexpected message type", shortID(message)));
				throw new RuntimeException("Received unexpected message type");
			}

			if (response.getType() == RESPONSE) {
				// Success response
				logger.debug(String.format("[%s]Received %s. Successfully queried chaincode",
						shortID(response.getTxid()), RESPONSE));
				return response.getPayload();
			}

			if (response.getType() == ERROR) {
				// Error response
				logger.error(String.format("[%s]Received %s.",
						shortID(response.getTxid()), ERROR));
				throw new RuntimeException(response.getPayload().toStringUtf8());
			}

			// Incorrect chaincode message received
			logger.error(String.format("[%s]Incorrect chaincode message %s recieved. Expecting %s or %s",
					shortID(response.getTxid()), response.getType(), RESPONSE, ERROR));
			throw new RuntimeException("Incorrect chaincode message received");
		} finally {
			deleteChannel(uuid);
		}
	}

	// handleMessage message handles loop for org.hyperledger.fabric.java.shim side of chaincode/validator stream.
	public synchronized void handleMessage(ChaincodeMessage message) throws Exception {

		if (message.getType() == ChaincodeMessage.Type.KEEPALIVE){
			logger.debug(String.format("[%s] Recieved KEEPALIVE message, do nothing",
					shortID(message)));
			// Received a keep alive message, we don't do anything with it for now
			// and it does not touch the state machine
				return;
		}

		logger.debug(String.format("[%s]Handling ChaincodeMessage of type: %s(state:%s)",
				shortID(message), message.getType(), fsm.current()));

		if (fsm.eventCannotOccur(message.getType().toString())) {
			String errStr = String.format("[%s]Chaincode handler org.hyperledger.fabric.java.fsm cannot handle message (%s) with payload size (%d) while in state: %s",
					message.getTxid(), message.getType(), message.getPayload().size(), fsm.current());
			ByteString payload = ByteString.copyFromUtf8(errStr);
			ChaincodeMessage errormessage = ChaincodeMessage.newBuilder()
					.setType(ERROR)
					.setPayload(payload)
					.setTxid(message.getTxid())
					.build();
			serialSend(errormessage);
			throw new RuntimeException(errStr);
		}

		// Filter errors to allow NoTransitionError and CanceledError
		// to not propagate for cases where embedded Err == nil.
		try {
			fsm.raiseEvent(message.getType().toString(), message);
		} catch (NoTransitionException e) {
			if (e.error != null) throw e;
			logger.debug("["+ shortID(message)+"]Ignoring NoTransitionError");
		} catch (CancelledException e) {
			if (e.error != null) throw e;
			logger.debug("["+ shortID(message)+"]Ignoring CanceledError");
		}
	}

	private String shortID(ChaincodeMessage message) {
		return shortID(message.getTxid());
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy