Skip to content

Commit

Permalink
Made check/uncheck of exceptions safer
Browse files Browse the repository at this point in the history
  • Loading branch information
spoto committed Nov 19, 2024
1 parent 5afc3c0 commit 032a638
Show file tree
Hide file tree
Showing 37 changed files with 219 additions and 243 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@

package io.hotmoka.instrumentation.internal;

import static io.hotmoka.exceptions.CheckRunnable.check;
import static io.hotmoka.exceptions.UncheckConsumer.uncheck;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
Expand Down Expand Up @@ -565,9 +562,13 @@ private void classLevelInstrumentations() {
* @throws ClassNotFoundException if some class of the Takamaka program cannot be found
*/
private void methodLevelInstrumentations() throws ClassNotFoundException {
check(ClassNotFoundException.class, () -> new ArrayList<>(methods).forEach(uncheck(this::preProcess)));
for (var method: new ArrayList<>(methods))
preProcess(method);

new DesugarBootstrapsInvokingEntries(this);
check(ClassNotFoundException.class, () -> new ArrayList<>(methods).forEach(uncheck(this::postProcess)));

for (var method: new ArrayList<>(methods))
postProcess(method);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public InstrumentedJarImpl(VerifiedJar verifiedJar, GasCostModel gasCostModel) t
// we cannot proceed in parallel since the BCEL library is not thread-safe
this.classes = check(ClassNotFoundException.class, () ->
verifiedJar.classes()
.map(uncheck(clazz -> InstrumentedClasses.of(clazz, gasCostModel)))
.map(uncheck(ClassNotFoundException.class, clazz -> InstrumentedClasses.of(clazz, gasCostModel)))
.collect(Collectors.toCollection(TreeSet::new))
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public class DesugarBootstrapsInvokingEntries extends ClassLevelInstrumentation
public DesugarBootstrapsInvokingEntries(InstrumentedClassImpl.Builder builder) throws ClassNotFoundException {
builder.super();
check(ClassNotFoundException.class, () ->
bootstraps.getBootstrapsLeadingToEntries().forEachOrdered(uncheck(this::desugarBootstrapCallingEntry))
bootstraps.getBootstrapsLeadingToEntries().forEachOrdered(uncheck(ClassNotFoundException.class, this::desugarBootstrapCallingEntry))
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public AddExtraArgsToCallsToFromContract(InstrumentedClassImpl.Builder builder,
InstructionList il = method.getInstructionList();
List<InstructionHandle> callsToFromContract = check(ClassNotFoundException.class, () ->
StreamSupport.stream(il.spliterator(), false)
.filter(uncheck(ih -> isCallToFromContract(ih.getInstruction()))).collect(Collectors.toList())
.filter(uncheck(ClassNotFoundException.class, ih -> isCallToFromContract(ih.getInstruction()))).collect(Collectors.toList())
);

for (InstructionHandle ih: callsToFromContract)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@

package io.hotmoka.instrumentation.internal.instrumentationsOfMethod;

import static io.hotmoka.exceptions.CheckRunnable.check;
import static io.hotmoka.exceptions.UncheckConsumer.uncheck;

import java.lang.reflect.Modifier;
import java.math.BigInteger;
import java.util.Comparator;
Expand Down Expand Up @@ -90,7 +87,8 @@ public AddGasUpdates(InstrumentedClassImpl.Builder builder, MethodGen method) th
CodeExceptionGen[] ceg = method.getExceptionHandlers();

dominators.forEach(dominator -> addCpuGasUpdate(dominator, il, ceg, dominators));
check(ClassNotFoundException.class, () -> il.forEach(uncheck(ih -> addRamGasUpdate(ih, il, ceg))));
for (var ih: il)
addRamGasUpdate(ih, il, ceg);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.apache.bcel.generic.PUTFIELD;
import org.apache.bcel.generic.Type;

import io.hotmoka.exceptions.UncheckConsumer;
import io.hotmoka.instrumentation.api.InstrumentationFields;
import io.hotmoka.instrumentation.internal.InstrumentedClassImpl;
import io.hotmoka.instrumentation.internal.InstrumentedClassImpl.Builder.MethodLevelInstrumentation;
Expand All @@ -60,8 +61,8 @@ public ReplaceFieldAccessesWithAccessors(InstrumentedClassImpl.Builder builder,
if (!method.isAbstract()) {
InstructionList il = method.getInstructionList();
check(ClassNotFoundException.class, () ->
StreamSupport.stream(il.spliterator(), false).filter(uncheck(this::isAccessToLazilyLoadedFieldInStorageClass))
.forEach(io.hotmoka.exceptions.UncheckConsumer.uncheck(ih -> ih.setInstruction(accessorCorrespondingTo((FieldInstruction) ih.getInstruction()))))
StreamSupport.stream(il.spliterator(), false).filter(uncheck(ClassNotFoundException.class, this::isAccessToLazilyLoadedFieldInStorageClass))
.forEach(UncheckConsumer.uncheck(ClassNotFoundException.class, ih -> ih.setInstruction(accessorCorrespondingTo((FieldInstruction) ih.getInstruction()))))
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ private DiskStoreTransformation restartTransaction(DiskStoreTransformation trans
if (transformation.deliveredCount() > 0) {
transformation.deliverRewardTransaction("", "");
storeOfHead = transformation.getFinalStore();
CheckRunnable.check(NodeException.class, () -> transformation.getDeliveredTransactions().forEachOrdered(UncheckConsumer.uncheck(reference -> publish(reference, storeOfHead))));
CheckRunnable.check(NodeException.class, () -> transformation.getDeliveredTransactions().forEachOrdered(UncheckConsumer.uncheck(NodeException.class, reference -> publish(reference, storeOfHead))));
}

return storeOfHead.beginTransformation(System.currentTimeMillis());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ public final Stream<Update> getState(StorageReference reference) throws UnknownR
try {
Stream<TransactionReference> history = store.getHistory(Objects.requireNonNull(reference));
var updates = new HashSet<Update>();
CheckRunnable.check(StoreException.class, () -> history.forEachOrdered(UncheckConsumer.uncheck(transaction -> addUpdatesCommitted(store, reference, transaction, updates))));
CheckRunnable.check(StoreException.class, () -> history.forEachOrdered(UncheckConsumer.uncheck(StoreException.class, transaction -> addUpdatesCommitted(store, reference, transaction, updates))));
return updates.stream();
}
catch (StoreException e) {
Expand Down Expand Up @@ -543,7 +543,7 @@ protected final void publish(TransactionReference reference, S store) throws Nod

try {
if (store.getResponse(reference) instanceof TransactionResponseWithEvents trwe)
CheckRunnable.check(NodeException.class, () -> trwe.getEvents().forEachOrdered(UncheckConsumer.uncheck(event -> notifyEvent(event, store))));
CheckRunnable.check(NodeException.class, () -> trwe.getEvents().forEachOrdered(UncheckConsumer.uncheck(NodeException.class, event -> notifyEvent(event, store))));
}
catch (StoreException | UnknownReferenceException e) {
throw new NodeException(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,8 +376,22 @@ protected final void setResponse(TransactionReference reference, TransactionResp
deltaResponses.put(reference, response);
}

protected final StorageReference getCreator(StorageReference event) throws UnknownReferenceException, FieldNotFoundException, StoreException {
return getReferenceField(event, FieldSignatures.EVENT_CREATOR_FIELD);
/**
* Yields the creator of the given event.
*
* @param event the reference to the event; this is assumed to be non-{@code null}
* and to actually refer an event
* @return the creator of {@code event}
* @throws StoreException if the store is corrupted
*/
protected final StorageReference getCreatorOfEvent(StorageReference event) throws StoreException {
try {
return getReferenceField(event, FieldSignatures.EVENT_CREATOR_FIELD);
}
catch (UnknownReferenceException | FieldNotFoundException e) {
// if it was possible to verify that it is an event, then it exists in store and must have a creator or otherwise the store is corrupted
throw new StoreException(e);
}
}

protected final BigInteger getCurrentSupply(StorageReference validators) throws UnknownReferenceException, FieldNotFoundException, StoreException {
Expand Down Expand Up @@ -441,18 +455,11 @@ else if (response instanceof TransactionResponseWithEvents trwe && trwe.getEvent
StorageReference validators = getValidators().orElseThrow(() -> new StoreException("The manifest is set but the validators are not set"));
StorageReference versions = getVersions().orElseThrow(() -> new StoreException("The manifest is set but the versions are not set"));
StorageReference gasStation = getGasStation().orElseThrow(() -> new StoreException("The manifest is set but gas station is not set"));
Stream<StorageReference> events = trwe.getEvents();

try {
return check(StoreException.class, UnknownReferenceException.class, FieldNotFoundException.class, () ->
events.filter(uncheck(event -> isConsensusUpdateEvent(event, classLoader)))
.map(UncheckFunction.uncheck(this::getCreator))

return check(StoreException.class, () ->
trwe.getEvents().filter(uncheck(StoreException.class, event -> isConsensusUpdateEvent(event, classLoader)))
.map(UncheckFunction.uncheck(StoreException.class, this::getCreatorOfEvent))
.anyMatch(creator -> creator.equals(manifest) || creator.equals(validators) || creator.equals(gasStation) || creator.equals(versions)));
}
catch (UnknownReferenceException | FieldNotFoundException e) {
// if it was possible to verify that it is an event, then it exists in store and must have a creator or otherwise the store is corrupted
throw new StoreException(e);
}
}
}

Expand Down Expand Up @@ -480,29 +487,12 @@ private boolean isConsensusUpdateEvent(StorageReference event, EngineClassLoader
* @throws ClassNotFoundException if some class of the Takamaka program cannot be loaded
*/
private boolean gasPriceMightHaveChanged(TransactionResponse response, EngineClassLoader classLoader) throws StoreException {
if (response instanceof InitializationTransactionResponse)
return true;
else if (response instanceof TransactionResponseWithEvents trwe && trwe.getEvents().findAny().isPresent()) {
Optional<StorageReference> maybeGasStation = getGasStation();

if (maybeGasStation.isPresent()) {
var gasStation = maybeGasStation.get();
Stream<StorageReference> events = trwe.getEvents();

try {
return check(StoreException.class, UnknownReferenceException.class, FieldNotFoundException.class, () ->
events.filter(uncheck(event -> isGasPriceUpdateEvent(event, classLoader)))
.map(UncheckFunction.uncheck(this::getCreator))
.anyMatch(gasStation::equals));
}
catch (UnknownReferenceException | FieldNotFoundException e) {
// if it was possible to verify that it is an event, then it exists in store and must have a creator or otherwise the store is corrupted
throw new StoreException(e);
}
}
}

return false;
return response instanceof InitializationTransactionResponse ||
(response instanceof TransactionResponseWithEvents trwe && trwe.getEvents().findAny().isPresent() &&
check(StoreException.class, () -> getGasStation().map(gasStation ->
trwe.getEvents().filter(uncheck(StoreException.class, event -> isGasPriceUpdateEvent(event, classLoader)))
.map(UncheckFunction.uncheck(StoreException.class, this::getCreatorOfEvent))
.anyMatch(gasStation::equals)).orElse(false)));
}

private boolean isGasPriceUpdateEvent(StorageReference event, EngineClassLoader classLoader) throws StoreException {
Expand All @@ -526,29 +516,12 @@ private boolean isGasPriceUpdateEvent(StorageReference event, EngineClassLoader
* @throws ClassNotFoundException if some class of the Takamaka program cannot be loaded
*/
private boolean inflationMightHaveChanged(TransactionResponse response, EngineClassLoader classLoader) throws StoreException {
if (response instanceof InitializationTransactionResponse)
return true;
else if (response instanceof TransactionResponseWithEvents trwe && trwe.getEvents().findAny().isPresent()) {
Optional<StorageReference> maybeValidators = getValidators();

if (maybeValidators.isPresent()) {
var validators = maybeValidators.get();
Stream<StorageReference> events = trwe.getEvents();

try {
return check(StoreException.class, UnknownReferenceException.class, FieldNotFoundException.class, () ->
events.filter(uncheck(event -> isInflationUpdateEvent(event, classLoader)))
.map(UncheckFunction.uncheck(this::getCreator))
.anyMatch(validators::equals));
}
catch (UnknownReferenceException | FieldNotFoundException e) {
// if it was possible to verify that it is an event, then it exists in store and must have a creator or otherwise the store is corrupted
throw new StoreException(e);
}
}
}

return false;
return response instanceof InitializationTransactionResponse ||
(response instanceof TransactionResponseWithEvents trwe && trwe.getEvents().findAny().isPresent() &&
check(StoreException.class, () -> getValidators().map(validators ->
trwe.getEvents().filter(uncheck(StoreException.class, event -> isInflationUpdateEvent(event, classLoader)))
.map(UncheckFunction.uncheck(StoreException.class, this::getCreatorOfEvent))
.anyMatch(validators::equals)).orElse(false)));
}

private boolean isInflationUpdateEvent(StorageReference event, EngineClassLoader classLoader) throws StoreException {
Expand Down Expand Up @@ -661,7 +634,7 @@ private void expandHistory(TransactionReference reference, TransactionResponseWi
response.getUpdates()
.map(Update::getObject)
.distinct()
.forEachOrdered(UncheckConsumer.uncheck(object -> setHistory(object, simplifiedHistory(object, reference, response.getUpdates())))));
.forEachOrdered(UncheckConsumer.uncheck(StoreException.class, object -> setHistory(object, simplifiedHistory(object, reference, response.getUpdates())))));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

import io.hotmoka.exceptions.CheckRunnable;
import io.hotmoka.exceptions.UncheckConsumer;
import io.hotmoka.exceptions.functions.ConsumerWithExceptions2;
import io.hotmoka.node.NonWhiteListedCallException;
import io.hotmoka.node.StorageValues;
import io.hotmoka.node.api.NodeException;
Expand Down Expand Up @@ -284,10 +285,11 @@ private boolean isStorage(Object object) {
* @throws NoSuchElementException
*/
private void argumentsAreExported() throws TransactionRejectedException, StoreException {
ConsumerWithExceptions2<StorageReference, TransactionRejectedException, StoreException> enforceExported = this::enforceExported;
CheckRunnable.check(TransactionRejectedException.class, StoreException.class, () -> request.actuals()
.filter(actual -> actual instanceof StorageReference)
.map(actual -> (StorageReference) actual)
.forEachOrdered(UncheckConsumer.uncheck(this::enforceExported)));
.forEachOrdered(UncheckConsumer.uncheck(TransactionRejectedException.class, StoreException.class, enforceExported)));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import io.hotmoka.exceptions.CheckSupplier;
import io.hotmoka.exceptions.UncheckFunction;
import io.hotmoka.exceptions.functions.FunctionWithExceptions2;
import io.hotmoka.node.NonWhiteListedCallException;
import io.hotmoka.node.TransactionReferences;
import io.hotmoka.node.TransactionResponses;
Expand All @@ -35,6 +36,7 @@
import io.hotmoka.node.api.signatures.ConstructorSignature;
import io.hotmoka.node.api.transactions.TransactionReference;
import io.hotmoka.node.api.values.StorageReference;
import io.hotmoka.node.api.values.StorageValue;
import io.hotmoka.node.local.DeserializationException;
import io.hotmoka.node.local.api.StoreException;

Expand Down Expand Up @@ -76,8 +78,9 @@ protected ConstructorCallTransactionResponse body() throws TransactionRejectedEx

try {
init();
FunctionWithExceptions2<StorageValue, ? extends Object, StoreException, DeserializationException> deserialize = deserializer::deserialize;
this.deserializedActuals = CheckSupplier.check(StoreException.class, DeserializationException.class,
() -> request.actuals().map(UncheckFunction.uncheck(deserializer::deserialize)).toArray(Object[]::new));
() -> request.actuals().map(UncheckFunction.uncheck(StoreException.class, DeserializationException.class, deserialize)).toArray(Object[]::new));

Object[] deserializedActuals;
Constructor<?> constructorJVM;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import io.hotmoka.exceptions.CheckSupplier;
import io.hotmoka.exceptions.UncheckFunction;
import io.hotmoka.exceptions.UncheckedException;
import io.hotmoka.exceptions.functions.FunctionWithExceptions2;
import io.hotmoka.node.api.UnknownReferenceException;
import io.hotmoka.node.api.signatures.FieldSignature;
import io.hotmoka.node.api.transactions.TransactionReference;
Expand Down Expand Up @@ -133,9 +134,11 @@ public Deserializer(ExecutionEnvironment environment, EngineClassLoader classLoa
* @throws StoreException if the operation could not be completed correctly
*/
public Object deserialize(StorageValue value) throws DeserializationException, StoreException {
if (value instanceof StorageReference sr)
if (value instanceof StorageReference sr) {
// we use a cache to provide the same value if the same reference gets deserialized twice
return CheckSupplier.check(DeserializationException.class, StoreException.class, () -> cache.computeIfAbsent(sr, UncheckFunction.uncheck(this::createStorageObject)));
FunctionWithExceptions2<StorageReference, ? extends Object, DeserializationException, StoreException> createStorageObject = this::createStorageObject;
return CheckSupplier.check(DeserializationException.class, StoreException.class, () -> cache.computeIfAbsent(sr, UncheckFunction.uncheck(DeserializationException.class, StoreException.class, createStorageObject)));
}
else if (value instanceof IntValue iv)
return iv.getValue();
else if (value instanceof BooleanValue bv)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

import io.hotmoka.exceptions.CheckRunnable;
import io.hotmoka.exceptions.UncheckConsumer;
import io.hotmoka.exceptions.functions.ConsumerWithExceptions2;
import io.hotmoka.instrumentation.api.InstrumentationFields;
import io.hotmoka.node.StorageTypes;
import io.hotmoka.node.api.TransactionRejectedException;
Expand Down Expand Up @@ -236,7 +237,10 @@ private TakamakaClassLoader mkTakamakaClassLoader(Stream<TransactionReference> d
counter.incrementAndGet();
}

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

processClassesInJars(jars, transactionsOfJars, environment);

try {
Expand Down Expand Up @@ -269,7 +273,9 @@ private void addJars(TransactionReference classpath, ConsensusConfig<?,?> consen
TransactionResponseWithInstrumentedJar responseWithInstrumentedJar = getResponseWithInstrumentedJarAt(classpath, environment);

// we consider its dependencies before as well, recursively
CheckRunnable.check(StoreException.class, () -> responseWithInstrumentedJar.getDependencies().forEachOrdered(UncheckConsumer.uncheck(dependency -> addJars(dependency, consensus, jars, jarTransactions, environment, counter))));
ConsumerWithExceptions2<TransactionReference, StoreException, TransactionRejectedException> addJars = dependency -> addJars(dependency, consensus, jars, jarTransactions, environment, counter);
CheckRunnable.check(StoreException.class, TransactionRejectedException.class,
() -> responseWithInstrumentedJar.getDependencies().forEachOrdered(UncheckConsumer.uncheck(StoreException.class, TransactionRejectedException.class, addJars)));

jars.add(responseWithInstrumentedJar.getInstrumentedJar());
jarTransactions.add(classpath);
Expand Down
Loading

0 comments on commit 032a638

Please sign in to comment.