Skip to content

Commit

Permalink
Fixed exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
spoto committed May 18, 2024
1 parent 77fd2fd commit ca06263
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,10 @@ public String toString() {

builder.append(" ├─ chainId: ").append(getChainId()).append("\n");

int maxErrorLength = ((IntValue) node.runInstanceMethodCallTransaction(TransactionRequests.instanceViewMethodCall // TODO: avoid casts
int maxErrorLength = node.runInstanceMethodCallTransaction(TransactionRequests.instanceViewMethodCall // TODO: avoid casts
(manifest, _100_000, takamakaCode, MethodSignatures.GET_MAX_ERROR_LENGTH, manifest))
.orElseThrow(() -> new NodeException(MethodSignatures.GET_MAX_ERROR_LENGTH + " should not return void"))).getValue();
.orElseThrow(() -> new NodeException(MethodSignatures.GET_MAX_ERROR_LENGTH + " should not return void"))
.asInt(value -> new NodeException(MethodSignatures.GET_MAX_ERROR_LENGTH + " should return an int, not a " + value.getClass().getName()));

builder.append(" ├─ maxErrorLength: ").append(maxErrorLength).append("\n");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
Expand All @@ -42,6 +41,7 @@
import io.hotmoka.exceptions.UncheckConsumer;
import io.hotmoka.instrumentation.api.InstrumentationFields;
import io.hotmoka.node.StorageTypes;
import io.hotmoka.node.api.TransactionRejectedException;
import io.hotmoka.node.api.UnknownReferenceException;
import io.hotmoka.node.api.nodes.ConsensusConfig;
import io.hotmoka.node.api.responses.TransactionResponse;
Expand Down Expand Up @@ -156,6 +156,8 @@ public final class EngineClassLoaderImpl implements EngineClassLoader {
* w.r.t. its version when the response has been added into the store.
*/
private final Reverification reverification;

private final ConsensusConfig<?, ?> consensus;

private final static int CLASS_END_LENGTH = ".class".length();

Expand All @@ -169,13 +171,12 @@ public final class EngineClassLoaderImpl implements EngineClassLoader {
* @throws ClassNotFoundException if some class of the dependencies cannot be found
* @throws StoreException
*/
public EngineClassLoaderImpl(byte[] jar, Stream<TransactionReference> dependencies, ExecutionEnvironment environment, ConsensusConfig<?,?> consensus) throws StoreException, ClassNotFoundException {
public EngineClassLoaderImpl(byte[] jar, Stream<TransactionReference> dependencies, ExecutionEnvironment environment, ConsensusConfig<?,?> consensus) throws StoreException, TransactionRejectedException {
try {
var dependenciesAsList = dependencies.collect(Collectors.toList());

// consensus might be null just after restarting a node, during the recomputation of the same consensus from the store;
// if reverification is not possible, we build an empty reverification object, for no dependencies
this.reverification = new Reverification(consensus != null ? dependenciesAsList.stream() : Stream.empty(), environment, consensus);
this.consensus = consensus;
this.reverification = new Reverification(dependenciesAsList.stream(), environment, consensus);
var jars = new ArrayList<byte[]>();
var transactionsOfJars = new ArrayList<TransactionReference>();
this.parent = mkTakamakaClassLoader(dependenciesAsList.stream(), consensus, jar, environment, jars, transactionsOfJars);
Expand Down Expand Up @@ -224,8 +225,9 @@ public EngineClassLoaderImpl(byte[] jar, Stream<TransactionReference> dependenci
* @return the class loader
* @throws ClassNotFoundException if some class of the Takamaka runtime cannot be loaded
* @throws StoreException
* @throws TransactionRejectedException
*/
private TakamakaClassLoader mkTakamakaClassLoader(Stream<TransactionReference> dependencies, ConsensusConfig<?,?> consensus, byte[] start, ExecutionEnvironment environment, List<byte[]> jars, ArrayList<TransactionReference> transactionsOfJars) throws ClassNotFoundException, StoreException {
private TakamakaClassLoader mkTakamakaClassLoader(Stream<TransactionReference> dependencies, ConsensusConfig<?,?> consensus, byte[] start, ExecutionEnvironment environment, List<byte[]> jars, ArrayList<TransactionReference> transactionsOfJars) throws StoreException, TransactionRejectedException {
var counter = new AtomicInteger();

if (start != null) {
Expand All @@ -234,16 +236,18 @@ private TakamakaClassLoader mkTakamakaClassLoader(Stream<TransactionReference> d
counter.incrementAndGet();
}

CheckRunnable.check(StoreException.class, () -> dependencies.forEachOrdered(UncheckConsumer.uncheck(dependency -> addJars(dependency, consensus, jars, transactionsOfJars, environment, counter))));
CheckRunnable.check(StoreException.class, TransactionRejectedException.class, () -> dependencies.forEachOrdered(UncheckConsumer.uncheck(dependency -> addJars(dependency, consensus, jars, transactionsOfJars, environment, counter))));
processClassInJar(jars, transactionsOfJars);

// consensus might be null if the node is restarting, during the recomputation of its consensus itself
try {
return TakamakaClassLoaders.of(jars.stream(), consensus != null ? consensus.getVerificationVersion() : 0);
return TakamakaClassLoaders.of(jars.stream(), consensus.getVerificationVersion());
}
catch (UnsupportedVerificationVersionException e) {
throw new StoreException(e);
}
catch (ClassNotFoundException e) {
throw new TransactionRejectedException(e, consensus);
}
}

/**
Expand All @@ -252,8 +256,9 @@ private TakamakaClassLoader mkTakamakaClassLoader(Stream<TransactionReference> d
*
* @param jars the jars that form the classpath of this classloader
* @param transactionsOfJars the transactions that have installed the {@code jars}
* @throws TransactionRejectedException
*/
private void processClassInJar(List<byte[]> jars, List<TransactionReference> transactionsOfJars) {
private void processClassInJar(List<byte[]> jars, List<TransactionReference> transactionsOfJars) throws TransactionRejectedException {
// a map from each package name to the jar that defines it
Map<String, Integer> packages = new HashMap<>();

Expand All @@ -268,17 +273,17 @@ private void processClassInJar(List<byte[]> jars, List<TransactionReference> tra
int lastDot = className.lastIndexOf('.');

if (lastDot == 0)
throw new IllegalArgumentException("package names cannot start with a dot");
throw new TransactionRejectedException("Package names cannot start with a dot");

String packageName = lastDot < 0 ? "" : className.substring(0, lastDot);
Integer previously = packages.get(packageName);
if (previously == null)
packages.put(packageName, pos);
else if (previously != pos)
if (packageName.isEmpty())
throw new IllegalArgumentException("the default package cannot be split across more jars");
throw new TransactionRejectedException("The default package cannot be split across more jars", consensus);
else
throw new IllegalArgumentException("package " + packageName + " cannot be split across more jars");
throw new TransactionRejectedException("Package " + packageName + " cannot be split across more jars", consensus);

// if the transaction reference is null, it means that the class comes from a jar that is being installed
// by the transaction that created this class loader. In that case, the storage reference of the class is not used
Expand All @@ -289,7 +294,8 @@ else if (previously != pos)
}
}
catch (IOException e) {
throw new UncheckedIOException(e);
// the jars seem corrupted
throw new TransactionRejectedException(e, consensus);
}

pos++;
Expand All @@ -305,11 +311,12 @@ else if (previously != pos)
* @param jarTransactions the list of transactions where the {@code jars} have been installed
* @param node the node for which the class loader is created
* @param counter the number of jars that have been encountered up to now, during the recursive descent
* @throws TransactionRejectedException
*/
private void addJars(TransactionReference classpath, ConsensusConfig<?,?> consensus, List<byte[]> jars, List<TransactionReference> jarTransactions, ExecutionEnvironment environment, AtomicInteger counter) throws StoreException {
private void addJars(TransactionReference classpath, ConsensusConfig<?,?> consensus, List<byte[]> jars, List<TransactionReference> jarTransactions, ExecutionEnvironment environment, AtomicInteger counter) throws StoreException, TransactionRejectedException {
// consensus might be null if the node is restarting, during the recomputation of its consensus itself
if (consensus != null && counter.incrementAndGet() > consensus.getMaxDependencies())
throw new IllegalArgumentException("too many dependencies in classpath: max is " + consensus.getMaxDependencies());
throw new TransactionRejectedException("Too many dependencies in classpath: max is " + consensus.getMaxDependencies(), consensus);

TransactionResponseWithInstrumentedJar responseWithInstrumentedJar = getResponseWithInstrumentedJarAtUncommitted(classpath, environment);

Expand All @@ -321,7 +328,7 @@ private void addJars(TransactionReference classpath, ConsensusConfig<?,?> consen

// consensus might be null if the node is restarting, during the recomputation of its consensus itself
if (consensus != null && jars.stream().mapToLong(bytes -> bytes.length).sum() > consensus.getMaxCumulativeSizeOfDependencies())
throw new IllegalArgumentException("too large cumulative size of dependencies in classpath: max is " + consensus.getMaxCumulativeSizeOfDependencies() + " bytes");
throw new TransactionRejectedException("Too large cumulative size of dependencies in classpath: max is " + consensus.getMaxCumulativeSizeOfDependencies() + " bytes", consensus);
}

/*
Expand All @@ -332,9 +339,9 @@ private void addJars(TransactionReference classpath, ConsensusConfig<?,?> consen
* @param reference the reference of the transaction
* @param node the node for which the class loader is created
* @return the response
* @throws IllegalArgumentException if the transaction does not exist in the store, or did not generate a response with instrumented jar
* @throws TransactionRejectedException if the transaction does not exist in the store, or did not generate a response with instrumented jar
*/
private TransactionResponseWithInstrumentedJar getResponseWithInstrumentedJarAtUncommitted(TransactionReference reference, ExecutionEnvironment environment) throws StoreException {
private TransactionResponseWithInstrumentedJar getResponseWithInstrumentedJarAtUncommitted(TransactionReference reference, ExecutionEnvironment environment) throws StoreException, TransactionRejectedException {
// first we check if the response has been reverified and we use the reverified version
Optional<TransactionResponse> maybeResponse = reverification.getReverifiedResponse(reference);
TransactionResponse response;
Expand All @@ -346,14 +353,14 @@ private TransactionResponseWithInstrumentedJar getResponseWithInstrumentedJarAtU
response = environment.getResponse(reference);
}
catch (UnknownReferenceException e) {
throw new IllegalArgumentException("unknown transaction reference " + reference); // TODO
throw new TransactionRejectedException("Unknown transaction reference " + reference, consensus);
}
}

if (response instanceof TransactionResponseWithInstrumentedJar trwij)
return trwij;
else
throw new IllegalArgumentException("The transaction " + reference + " did not install a jar in store"); // TODO
throw new TransactionRejectedException("The transaction " + reference + " did not install a jar in store", consensus);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -537,15 +537,8 @@ protected final Optional<BigInteger> getGasPrice() throws StoreException {

protected abstract Hasher<TransactionRequest<?>> getHasher();

private EngineClassLoader mkClassLoader(TransactionReference classpath, ConsensusConfig<?,?> consensus) throws StoreException {
try {
return new EngineClassLoaderImpl(null, Stream.of(classpath), this, consensus);
}
catch (ClassNotFoundException e) {
// since the class loader is created from transactions that are already in the store,
// they should be consistent and never miss a dependent class
throw new StoreException(e); // TODO: really?
}
private EngineClassLoader mkClassLoader(TransactionReference classpath, ConsensusConfig<?,?> consensus) throws StoreException, TransactionRejectedException {
return new EngineClassLoaderImpl(null, Stream.of(classpath), this, consensus);
}

private boolean verifySignature(SignatureAlgorithm signature, SignedTransactionRequest<?> request) throws StoreException, UnknownReferenceException, FieldNotFoundException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,7 @@ public JarStoreInitialResponseBuilder(TransactionReference reference, JarStoreIn
protected EngineClassLoader mkClassLoader() throws StoreException, TransactionRejectedException {
// we redefine this method, since the class loader must be able to access the
// jar that is being installed and its dependencies, in order to instrument them
try {
return new EngineClassLoaderImpl(request.getJar(), request.getDependencies(), environment, consensus);
}
catch (ClassNotFoundException | IllegalArgumentException e) {
// the request is trying to install a jar with inconsistent dependencies
throw new TransactionRejectedException(e, consensus);
}
return new EngineClassLoaderImpl(request.getJar(), request.getDependencies(), environment, consensus);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,7 @@ public JarStoreResponseBuilder(TransactionReference reference, JarStoreTransacti
protected EngineClassLoader mkClassLoader() throws StoreException, TransactionRejectedException {
// we redefine this method, since the class loader must be able to access the
// jar that is being installed and its dependencies, in order to instrument them
try {
return new EngineClassLoaderImpl(request.getJar(), request.getDependencies(), environment, consensus);
}
catch (ClassNotFoundException | IllegalArgumentException e) {
// the request is trying to install a jar with inconsistent dependencies
throw new TransactionRejectedException(e, consensus);
}
return new EngineClassLoaderImpl(request.getJar(), request.getDependencies(), environment, consensus);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.hotmoka.tests;

import static java.math.BigInteger.ONE;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.io.IOException;
import java.math.BigInteger;
Expand Down Expand Up @@ -56,7 +57,7 @@ void testDisjointJars() throws TransactionException, TransactionRejectedExceptio

@Test @DisplayName("jars with packages split among them cannot be put together")
void testSplitPackages() {
throwsTransactionRejectedWithCause(IllegalArgumentException.class, () ->
assertThrows(TransactionRejectedException.class, () ->
addJarStoreTransaction(privateKey(0), account(0), _1_000_000, ONE, takamakaCode(), bytesOf("basicdependency.jar"), jar()));
}
}

0 comments on commit ca06263

Please sign in to comment.