Skip to content

Commit

Permalink
Further clean-up of the API of the store transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
spoto committed May 10, 2024
1 parent 68fdd63 commit 16b423b
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ public DiskStoreTransaction(DiskStore store, ExecutorService executors, Consensu
}

@Override
public Optional<TransactionResponse> getResponseUncommitted(TransactionReference reference) {
public TransactionResponse getResponseUncommitted(TransactionReference reference) throws UnknownReferenceException {
var uncommittedResponse = responses.get(reference);
if (uncommittedResponse != null)
return Optional.of(uncommittedResponse);
return uncommittedResponse;
else
return getStore().getResponse(reference);
return getStore().getResponse(reference).orElseThrow(() -> new UnknownReferenceException(reference));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,14 @@ public interface StoreTransaction<S extends Store<S,T>, T extends StoreTransacti
*/
ConsensusConfig<?,?> getConfigUncommitted() throws StoreException;

<X> Future<X> submit(Callable<X> task);

/**
* Yields the response of the transaction having the given reference.
* This considers also updates inside this transaction, that have not yet been committed.
*
* @param reference the reference of the transaction
* @return the response, if any
* @return the response
*/
Optional<TransactionResponse> getResponseUncommitted(TransactionReference reference) throws StoreException;
TransactionResponse getResponseUncommitted(TransactionReference reference) throws UnknownReferenceException, StoreException;

/**
* Yields the history of the given object, that is, the references to the transactions
Expand All @@ -119,8 +117,6 @@ public interface StoreTransaction<S extends Store<S,T>, T extends StoreTransacti

Optional<TransactionReference> getTakamakaCodeUncommitted() throws StoreException;

boolean nodeIsInitializedUncommitted() throws StoreException;

Optional<StorageReference> getValidatorsUncommitted() throws StoreException;

Optional<StorageReference> getGameteUncommitted() throws StoreException;
Expand Down Expand Up @@ -158,6 +154,8 @@ public interface StoreTransaction<S extends Store<S,T>, T extends StoreTransacti
*/
void rewardValidators(String behaving, String misbehaving) throws StoreException;

<X> Future<X> submit(Callable<X> task);

void invalidateConsensusCache() throws StoreException;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -890,11 +890,6 @@ public final Optional<TransactionReference> getTakamakaCodeUncommitted() throws
}
}

@Override
public final boolean nodeIsInitializedUncommitted() throws StoreException {
return getManifestUncommitted().isPresent();
}

private Optional<StorageReference> getGasStationUncommitted() throws StoreException {
var maybeManifest = getManifestUncommitted();
if (maybeManifest.isPresent()) {
Expand Down Expand Up @@ -1005,10 +1000,7 @@ public final String getClassNameUncommitted(StorageReference reference) throws U
@Override
public final ClassTag getClassTagUncommitted(StorageReference reference) throws UnknownReferenceException, StoreException {
// we go straight to the transaction that created the object
Optional<TransactionResponse> maybeResponse = getResponseUncommitted(reference.getTransaction());
if (maybeResponse.isEmpty())
throw new UnknownReferenceException("Object " + reference + " does not exist");
else if (maybeResponse.get() instanceof TransactionResponseWithUpdates trwu) {
if (getResponseUncommitted(reference.getTransaction()) instanceof TransactionResponseWithUpdates trwu) {
return trwu.getUpdates().filter(update -> update instanceof ClassTag && update.getObject().equals(reference))
.map(update -> (ClassTag) update)
.findFirst()
Expand Down Expand Up @@ -1165,14 +1157,16 @@ private PublicKey getPublicKeyUncommitted(StorageReference reference, SignatureA
*/
protected abstract void setManifest(StorageReference manifest) throws StoreException;

private Stream<Update> getUpdates(TransactionReference reference) throws StoreException {
Optional<TransactionResponse> maybeResponse = getResponseUncommitted(reference);
if (maybeResponse.isEmpty())
throw new StoreException("Transaction " + maybeResponse.get() + " belongs to the histories but is not present in store");
else if (maybeResponse.get() instanceof TransactionResponseWithUpdates trwu)
return trwu.getUpdates();
else
throw new StoreException("Transaction " + maybeResponse.get() + " belongs to the histories but does not contain updates");
private Stream<Update> getUpdates(TransactionReference referenceInHistory) throws StoreException {
try {
if (getResponseUncommitted(referenceInHistory) instanceof TransactionResponseWithUpdates trwu)
return trwu.getUpdates();
else
throw new StoreException("Transaction " + referenceInHistory + " belongs to the histories but does not contain updates");
}
catch (UnknownReferenceException e) {
throw new StoreException("Transaction " + referenceInHistory + " belongs to the histories but is not present in store");
}
}

private StorageReference getReferenceFieldUncommitted(StorageReference object, FieldSignature field) throws UnknownReferenceException, FieldNotFoundException, StoreException {
Expand Down Expand Up @@ -1209,7 +1203,7 @@ private String getStringFieldUncommitted(StorageReference object, FieldSignature
* the {@code transaction}, this method returns an empty optional
*/
private Optional<UpdateOfField> getLastUpdateUncommitted(StorageReference object, FieldSignature field, TransactionReference reference) throws UnknownReferenceException, StoreException {
if (getResponseUncommitted(reference).orElseThrow(() -> new UnknownReferenceException(reference)) instanceof TransactionResponseWithUpdates trwu)
if (getResponseUncommitted(reference) instanceof TransactionResponseWithUpdates trwu)
return trwu.getUpdates()
.filter(update -> update instanceof UpdateOfField)
.map(update -> (UpdateOfField) update)
Expand All @@ -1230,14 +1224,19 @@ private Optional<UpdateOfField> getLastUpdateUncommitted(StorageReference object
* the {@code reference}, this method returns an empty optional
*/
private Optional<UpdateOfField> getLastUpdateMustExistUncommitted(StorageReference object, FieldSignature field, TransactionReference reference) throws StoreException {
if (getResponseUncommitted(reference).orElseThrow(() -> new StoreException("Object " + object + " is part of the history but cannot be found in store")) instanceof TransactionResponseWithUpdates trwu)
return trwu.getUpdates()
.filter(update -> update instanceof UpdateOfField)
.map(update -> (UpdateOfField) update)
.filter(update -> update.getObject().equals(object) && update.getField().equals(field))
.findFirst();
else
throw new StoreException("Transaction reference " + reference + " does not contain updates");
try {
if (getResponseUncommitted(reference) instanceof TransactionResponseWithUpdates trwu)
return trwu.getUpdates()
.filter(update -> update instanceof UpdateOfField)
.map(update -> (UpdateOfField) update)
.filter(update -> update.getObject().equals(object) && update.getField().equals(field))
.findFirst();
else
throw new StoreException("Transaction reference " + reference + " does not contain updates");
}
catch (UnknownReferenceException e) {
throw new StoreException("Object " + object + " is part of the history but cannot be found in store");
}
}

/**
Expand Down Expand Up @@ -1265,20 +1264,20 @@ private void expandHistory(TransactionReference reference, TransactionResponseWi
* of some fields are updated in {@code added} and the useless old history element provided only values
* for the newly updated fields.
*
* @param object the object whose history is being simplified
* @param objectUpdatedInResponse the object whose history is being simplified
* @param added the transaction reference to add in front of the history of {@code object}
* @param addedUpdates the updates generated in {@code added}
* @return the simplified history, with {@code added} in front followed by a subset of the old history
*/
private Stream<TransactionReference> simplifiedHistory(StorageReference object, TransactionReference added, Stream<Update> addedUpdates) throws StoreException {
private Stream<TransactionReference> simplifiedHistory(StorageReference objectUpdatedInResponse, TransactionReference added, Stream<Update> addedUpdates) throws StoreException {
// if the object has been created at the added transaction, that is its history
if (object.getTransaction().equals(added))
if (objectUpdatedInResponse.getTransaction().equals(added))
return Stream.of(added);

Stream<TransactionReference> old;

try {
old = getHistoryUncommitted(object);
old = getHistoryUncommitted(objectUpdatedInResponse);
}
catch (UnknownReferenceException e) {
// the object was created before this transaction: it must have a history or otherwise the store is corrupted
Expand All @@ -1287,14 +1286,14 @@ private Stream<TransactionReference> simplifiedHistory(StorageReference object,

// we trace the set of updates that are already covered by previous transactions, so that
// subsequent history elements might become unnecessary, since they do not add any yet uncovered update
Set<Update> covered = addedUpdates.filter(update -> update.getObject().equals(object)).collect(Collectors.toSet());
var simplified = new ArrayList<TransactionReference>();
Set<Update> covered = addedUpdates.filter(update -> update.getObject().equals(objectUpdatedInResponse)).collect(Collectors.toSet());
var simplified = new ArrayList<TransactionReference>(10);
simplified.add(added);

var oldAsArray = old.toArray(TransactionReference[]::new);
int lastPos = oldAsArray.length - 1;
for (int pos = 0; pos < lastPos; pos++)
addIfUncovered(oldAsArray[pos], object, covered, simplified);
addIfUncovered(oldAsArray[pos], objectUpdatedInResponse, covered, simplified);

// the last is always useful, since it contains at least the class tag of the object
if (lastPos >= 0)
Expand All @@ -1307,23 +1306,13 @@ private Stream<TransactionReference> simplifiedHistory(StorageReference object,
* Adds the given transaction reference to the history of the given object,
* if it provides updates for fields that have not yet been covered by other updates.
*
* @param reference the transaction reference
* @param referenceInHistory the transaction reference
* @param object the object
* @param covered the set of updates for the already covered fields
* @param history the history; this might be modified by the method, by prefixing {@code reference} at its front
*/
private void addIfUncovered(TransactionReference reference, StorageReference object, Set<Update> covered, List<TransactionReference> history) throws StoreException {
Optional<TransactionResponse> maybeResponse = getResponseUncommitted(reference);

if (maybeResponse.isEmpty())
throw new StoreException("The history contains a reference to a transaction not in store");
else if (maybeResponse.get() instanceof TransactionResponseWithUpdates trwu) {
// we check if there is at least an update for a field of the object
// that is not yet covered by another update in a previous element of the history
if (trwu.getUpdates().filter(update -> update.getObject().equals(object) && covered.stream().noneMatch(update::sameProperty) && covered.add(update)).count() > 0)
history.add(reference);
}
else
throw new StoreException("The history contains a reference to a transaction without updates");
private void addIfUncovered(TransactionReference referenceInHistory, StorageReference object, Set<Update> covered, List<TransactionReference> history) throws StoreException {
if (getUpdates(referenceInHistory).filter(update -> update.getObject().equals(object) && covered.stream().noneMatch(update::sameProperty) && covered.add(update)).count() > 0)
history.add(referenceInHistory);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ protected AbstractTrieBasedStoreTransaction(S store, ExecutorService executors,
}

@Override
public Optional<TransactionResponse> getResponseUncommitted(TransactionReference reference) throws StoreException{
public TransactionResponse getResponseUncommitted(TransactionReference reference) throws UnknownReferenceException, StoreException {
try {
return trieOfResponses.get(reference);
return trieOfResponses.get(reference).orElseThrow(() -> new UnknownReferenceException(reference));
}
catch (TrieException e) {
throw new StoreException(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import io.hotmoka.exceptions.UncheckSupplier;
import io.hotmoka.exceptions.CheckRunnable;
import io.hotmoka.exceptions.UncheckConsumer;
import io.hotmoka.instrumentation.api.InstrumentationFields;
import io.hotmoka.node.StorageTypes;
import io.hotmoka.node.api.NodeException;
Expand Down Expand Up @@ -224,8 +225,9 @@ public EngineClassLoaderImpl(byte[] jar, Stream<TransactionReference> dependenci
* @param node the node for which the class loader is created
* @return the class loader
* @throws ClassNotFoundException if some class of the Takamaka runtime cannot be loaded
* @throws StoreException
*/
private TakamakaClassLoader mkTakamakaClassLoader(Stream<TransactionReference> dependencies, ConsensusConfig<?,?> consensus, byte[] start, StoreTransaction<?,?> storeTransaction, List<byte[]> jars, ArrayList<TransactionReference> transactionsOfJars) throws ClassNotFoundException {
private TakamakaClassLoader mkTakamakaClassLoader(Stream<TransactionReference> dependencies, ConsensusConfig<?,?> consensus, byte[] start, StoreTransaction<?,?> storeTransaction, List<byte[]> jars, ArrayList<TransactionReference> transactionsOfJars) throws ClassNotFoundException, StoreException {
var counter = new AtomicInteger();

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

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

// consensus might be null if the node is restarting, during the recomputation of its consensus itself
Expand Down Expand Up @@ -301,15 +303,15 @@ else if (previously != pos)
* @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
*/
private void addJars(TransactionReference classpath, ConsensusConfig<?,?> consensus, List<byte[]> jars, List<TransactionReference> jarTransactions, StoreTransaction<?,?> storeTransaction, AtomicInteger counter) {
private void addJars(TransactionReference classpath, ConsensusConfig<?,?> consensus, List<byte[]> jars, List<TransactionReference> jarTransactions, StoreTransaction<?,?> storeTransaction, AtomicInteger counter) throws StoreException {
// 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());

TransactionResponseWithInstrumentedJar responseWithInstrumentedJar = getResponseWithInstrumentedJarAtUncommitted(classpath, storeTransaction);

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

jars.add(responseWithInstrumentedJar.getInstrumentedJar());
jarTransactions.add(classpath);
Expand All @@ -329,12 +331,21 @@ private void addJars(TransactionReference classpath, ConsensusConfig<?,?> consen
* @return the response
* @throws IllegalArgumentException if the transaction does not exist in the store, or did not generate a response with instrumented jar
*/
private TransactionResponseWithInstrumentedJar getResponseWithInstrumentedJarAtUncommitted(TransactionReference reference, StoreTransaction<?,?> storeTransaction) {
private TransactionResponseWithInstrumentedJar getResponseWithInstrumentedJarAtUncommitted(TransactionReference reference, StoreTransaction<?,?> storeTransaction) throws StoreException {
// first we check if the response has been reverified and we use the reverified version
TransactionResponse response = reverification.getReverifiedResponse(reference)
// otherwise the response has not been reverified
.or(UncheckSupplier.uncheck(() -> storeTransaction.getResponseUncommitted(reference))) // TODO: recheck
.orElseThrow(() -> new IllegalArgumentException("unknown transaction reference " + reference));
Optional<TransactionResponse> maybeResponse = reverification.getReverifiedResponse(reference);
TransactionResponse response;
if (maybeResponse.isPresent())
response = maybeResponse.get();
else {
// otherwise the response has not been reverified
try {
response = storeTransaction.getResponseUncommitted(reference);
}
catch (UnknownReferenceException e) {
throw new IllegalArgumentException("unknown transaction reference " + reference); // TODO
}
}

if (response instanceof TransactionResponseWithInstrumentedJar trwij)
return trwij;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

import io.hotmoka.node.TransactionResponses;
import io.hotmoka.node.api.NodeException;
import io.hotmoka.node.api.UnknownReferenceException;
import io.hotmoka.node.api.nodes.ConsensusConfig;
import io.hotmoka.node.api.requests.GenericJarStoreTransactionRequest;
import io.hotmoka.node.api.requests.InitialTransactionRequest;
Expand Down Expand Up @@ -261,12 +262,14 @@ private GenericJarStoreTransactionResponse updateVersion(TransactionResponseWith
* @throws StoreException if the transaction does not exist in the store, or did not generate a response with instrumented jar
*/
private TransactionResponseWithInstrumentedJar getResponseWithInstrumentedJarAtUncommitted(TransactionReference reference) throws StoreException {
TransactionResponse response = storeTransaction.getResponseUncommitted(reference)
.orElseThrow(() -> new StoreException("Unknown transaction reference " + reference + " under reverification"));

if (response instanceof TransactionResponseWithInstrumentedJar trwij)
return trwij;
else
throw new StoreException("The transaction " + reference + " under reverification did not install a jar in store");
try {
if (storeTransaction.getResponseUncommitted(reference) instanceof TransactionResponseWithInstrumentedJar trwij)
return trwij;
else
throw new StoreException("The transaction " + reference + " under reverification did not install a jar in store");
}
catch (UnknownReferenceException e) {
throw new StoreException("Unknown transaction reference " + reference + " under reverification"); // TODO: is this the right exception?
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ protected InitialResponseBuilderImpl(TransactionReference reference, Request req
super(reference, request, storeTransaction);

try {
if (storeTransaction.nodeIsInitializedUncommitted())
if (storeTransaction.getManifestUncommitted().isPresent())
throw new TransactionRejectedException("Cannot run an initial transaction request in an already initialized node");
}
catch (Throwable t) {
Expand Down
Loading

0 comments on commit 16b423b

Please sign in to comment.