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

io.hotmoka.node.local.internal.builders.AbstractResponseBuilder Maven / Gradle / Ivy

/*
Copyright 2021 Fausto Spoto

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 io.hotmoka.node.local.internal.builders;

import static io.hotmoka.node.local.internal.runtime.Runtime.responseCreators;

import java.io.IOException;
import java.math.BigInteger;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

import io.hotmoka.node.OutOfGasError;
import io.hotmoka.node.StorageValues;
import io.hotmoka.node.api.TransactionRejectedException;
import io.hotmoka.node.api.UnknownReferenceException;
import io.hotmoka.node.api.nodes.ConsensusConfig;
import io.hotmoka.node.api.requests.SystemTransactionRequest;
import io.hotmoka.node.api.requests.TransactionRequest;
import io.hotmoka.node.api.responses.TransactionResponse;
import io.hotmoka.node.api.signatures.FieldSignature;
import io.hotmoka.node.api.transactions.TransactionReference;
import io.hotmoka.node.api.values.StorageReference;
import io.hotmoka.node.local.DeserializationException;
import io.hotmoka.node.local.api.EngineClassLoader;
import io.hotmoka.node.local.api.FieldNotFoundException;
import io.hotmoka.node.local.api.ResponseBuilder;
import io.hotmoka.node.local.api.StoreException;
import io.hotmoka.verification.VerificationException;
import io.hotmoka.whitelisting.api.UnsupportedVerificationVersionException;

/**
 * A generic implementation of the creator of a response.
 *
 * @param  the type of the request of the transaction
 * @param  the type of the response of the transaction
 */
public abstract class AbstractResponseBuilder, Response extends TransactionResponse> implements ResponseBuilder {

	/**
	 * The reference used to refer to the transaction generated by the request.
	 */
	private final TransactionReference reference;

	/**
	 * The execution environment where the response is built.
	 */
	protected final ExecutionEnvironment environment;

	/**
	 * The class loader used for the transaction.
	 */
	protected final EngineClassLoader classLoader;

	/**
	 * The request of the transaction.
	 */
	protected final Request request;

	/**
	 * The consensus parameters when this builder was created.
	 */
	protected final ConsensusConfig consensus;

	/**
	 * Creates the builder of a response.
	 * 
	 * @param reference the reference to the transaction that is building the response
	 * @param request the request for which the response is being built
	 * @param environment the execution environment where the response is built
	 * @throws TransactionRejectedException if the builder cannot be created
	 * @throws StoreException if the operation cannot be completed correctly
	 */
	protected AbstractResponseBuilder(TransactionReference reference, Request request, ExecutionEnvironment environment) throws TransactionRejectedException, StoreException {
		this.environment = environment;
		this.request = request;
		this.reference = reference;
		this.consensus = environment.getConfig();
		this.classLoader = mkClassLoader();
	}

	@Override
	public final Request getRequest() {
		return request;
	}

	@Override
	public final EngineClassLoader getClassLoader() {
		return classLoader;
	}

	@Override
	public final void replaceReverifiedResponses() throws StoreException {
		((EngineClassLoaderImpl) classLoader).replaceReverifiedResponses();
	}

	/**
	 * Creates the class loader for computing the response.
	 * 
	 * @return the class loader
	 * @throws ClassNotFoundException if some class of the Takamaka runtime cannot be loaded
	 * @throws UnsupportedVerificationVersionException if the verification version is not available
	 * @throws IOException if there was an I/O error while accessing some jar
	 */
	protected abstract EngineClassLoader mkClassLoader() throws StoreException, TransactionRejectedException;

	/**
	 * The creator of a response. Its body runs in a thread, so that the
	 * {@linkplain io.hotmoka.node.local.internal.runtime.Runtime} class
	 * can recover it from its thread-local table.
	 */
	public abstract class ResponseCreator {

		/**
		 * The object that deserializes storage objects into RAM values.
		 */
		protected final Deserializer deserializer;

		/**
		 * The object that can be used to extract the updates to a set of storage objects,
		 * induced by the run of the transaction.
		 */
		protected final UpdatesExtractor updatesExtractor;

		/**
		 * The counter for the next storage object created during the transaction.
		 */
		private BigInteger nextProgressive = BigInteger.ZERO;

		protected ResponseCreator() {
			this.deserializer = new Deserializer(environment, classLoader);
			this.updatesExtractor = new UpdatesExtractor(classLoader);
		}

		public final Response create() throws StoreException {
			try {
				return environment.submit(new TakamakaCallable(this::body)).get();
			}
			catch (ExecutionException e) {
				throw new StoreException(e.getCause());
			}
			catch (InterruptedException e) {
				// TODO
				throw new RuntimeException(e);
			}
		}

		/**
		 * The body of the creation of the response.
		 * 
		 * @return the response
		 * @throws ClassNotFoundException if some class of the Takamaka program cannot be found
		 * @throws UnsupportedVerificationVersionException if the verification version is not available
		 * @throws VerificationException if the verification of a jar failed, before being installed in the node
		 */
		protected abstract Response body() throws ClassNotFoundException, UnsupportedVerificationVersionException, VerificationException;

		/**
		 * Yields the UTC time when the transaction is being run.
		 * This might be for instance the time of creation of a block where the transaction
		 * will be stored, but the detail is left to the implementation.
		 * 
		 * @return the UTC time, as returned by {@link java.lang.System#currentTimeMillis()}
		 */
		public final long now() {
			return environment.getNow();
		}

		/**
		 * Determines if the execution was started by the node itself.
		 * This is always false if the node has no notion of commit.
		 * If the execution has been started by a user request, this will
		 * always be false.
		 * 
		 * @return true if and only if that condition occurs
		 */
		public final boolean isSystemCall() {
			return request instanceof SystemTransactionRequest;
		}

		/**
		 * Takes note of the given event, emitted during this execution.
		 * 
		 * @param event the event
		 */
		public abstract void event(Object event);

		/**
		 * Runs a given piece of code with a subset of the available gas.
		 * It first charges the given amount of gas. Then runs the code
		 * with the charged gas only. At its end, the remaining gas is added
		 * to the available gas to continue the computation.
		 * 
		 * @param amount the amount of gas provided to the code
		 * @param what the code to run
		 * @return the result of the execution of the code
		 * @throws OutOfGasError if there is not enough gas
		 * @throws Exception if the code runs into this exception
		 */
		public abstract  T withGas(BigInteger amount, Callable what) throws Exception;

		/**
		 * Decreases the available gas by the given amount, for CPU execution.
		 * 
		 * @param amount the amount of gas to consume
		 */
		public abstract void chargeGasForCPU(BigInteger amount);

		/**
		 * Decreases the available gas by the given amount, for RAM execution.
		 * 
		 * @param amount the amount of gas to consume
		 */
		public abstract void chargeGasForRAM(BigInteger amount);

		/**
		 * Yields the latest value for the given field of the object with the given storage reference.
		 * The field is not {@code final}. Conceptually, this method looks for the value of the field
		 * in the last transaction where the reference was updated.
		 * 
		 * @param object the storage reference
		 * @param field the field
		 * @return the value of the field
		 */
		public final Object deserializeLastUpdateFor(StorageReference object, FieldSignature field) throws DeserializationException, StoreException {
			try {
				return deserializer.deserialize(environment.getLastUpdateToField(object, field).getValue());
			}
			catch (UnknownReferenceException | FieldNotFoundException e) {
				throw new DeserializationException(e);
			}
		}

		/**
		 * Yields the latest value for the given field of the object with the given storage reference.
		 * The field is {@code final}. Conceptually, this method looks for the value of the field
		 * in the transaction where the reference was created.
		 * 
		 * @param object the storage reference
		 * @param field the field
		 * @return the value of the field
		 */
		public final Object deserializeLastUpdateForFinal(StorageReference object, FieldSignature field) throws DeserializationException, StoreException {
			try {
				return deserializer.deserialize(environment.getLastUpdateToFinalField(object, field).getValue());
			}
			catch (UnknownReferenceException | FieldNotFoundException e) {
				throw new DeserializationException(e);
			}
		}

		/**
		 * Yields the next storage reference for the current transaction.
		 * This can be used to associate a storage reference to each new
		 * storage object created during a transaction.
		 * 
		 * @return the next storage reference
		 */
		public final StorageReference getNextStorageReference() {
			BigInteger result = nextProgressive;
			nextProgressive = nextProgressive.add(BigInteger.ONE);
			return StorageValues.reference(reference, result);
		}

		/**
		 * Yields the class loader used for the transaction being created.
		 * 
		 * @return the class loader
		 */
		public final EngineClassLoaderImpl getClassLoader() {
			return (EngineClassLoaderImpl) classLoader;
		}

		/**
		 * A task that executes Takamaka code as part of this transaction.
		 * It sets the response creator in the thread-local of the runtime.
		 */
		private final class TakamakaCallable implements Callable {
			private final Callable body;

			private TakamakaCallable(Callable body) {
				this.body = body;
			}

			@Override
			public Response call() throws Exception {
				try {
					responseCreators.set(ResponseCreator.this);
					return body.call();
				}
				finally {
					responseCreators.remove();
				}
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy