Skip to content

Commit 6325829

Browse files
committed
Clean-up in the local nodes
1 parent c3f3252 commit 6325829

File tree

10 files changed

+155
-65
lines changed

10 files changed

+155
-65
lines changed

io-hotmoka-node-disk/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
<artifactId>io-hotmoka-node-disk-api</artifactId>
2121
<version>${hotmoka.version}</version>
2222
</dependency>
23+
<dependency>
24+
<groupId>io.hotmoka</groupId>
25+
<artifactId>io-hotmoka-node-api</artifactId>
26+
<version>${hotmoka.version}</version>
27+
</dependency>
2328
<dependency>
2429
<groupId>io.hotmoka</groupId>
2530
<artifactId>io-hotmoka-node-local</artifactId>

io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/DiskNodeImpl.java

+12-3
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@
2424
import java.util.logging.Logger;
2525

2626
import io.hotmoka.annotations.ThreadSafe;
27-
import io.hotmoka.exceptions.CheckRunnable;
28-
import io.hotmoka.exceptions.UncheckConsumer;
2927
import io.hotmoka.node.ClosedNodeException;
3028
import io.hotmoka.node.NodeInfos;
3129
import io.hotmoka.node.api.NodeException;
3230
import io.hotmoka.node.api.TransactionRejectedException;
31+
import io.hotmoka.node.api.UnknownReferenceException;
3332
import io.hotmoka.node.api.nodes.NodeInfo;
3433
import io.hotmoka.node.api.requests.TransactionRequest;
34+
import io.hotmoka.node.api.transactions.TransactionReference;
3535
import io.hotmoka.node.disk.api.DiskNode;
3636
import io.hotmoka.node.disk.api.DiskNodeConfig;
3737
import io.hotmoka.node.local.AbstractLocalNode;
@@ -247,7 +247,16 @@ private DiskStoreTransformation restartTransaction(DiskStoreTransformation trans
247247
if (transformation.deliveredCount() > 0) {
248248
transformation.deliverRewardTransaction("", "");
249249
storeOfHead = transformation.getFinalStore();
250-
CheckRunnable.check(NodeException.class, () -> transformation.getDeliveredTransactions().forEachOrdered(UncheckConsumer.uncheck(NodeException.class, reference -> publish(reference, storeOfHead))));
250+
var transactions = transformation.getDeliveredTransactions().toArray(TransactionReference[]::new);
251+
for (var transaction: transactions) {
252+
try {
253+
publish(transaction, storeOfHead);
254+
}
255+
catch (UnknownReferenceException e) {
256+
// the transactions have been delivered, if they cannot be found then there is a problem in the database
257+
throw new NodeException("Delivered transactions should be in store", e);
258+
}
259+
}
251260
}
252261

253262
return storeOfHead.beginTransformation(System.currentTimeMillis());

io-hotmoka-node-disk/src/main/java/module-info.java

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
exports io.hotmoka.node.disk;
2323

2424
requires transitive io.hotmoka.node.disk.api;
25+
requires io.hotmoka.node.api;
2526
requires transitive io.hotmoka.node.local.api;
2627
requires io.hotmoka.annotations;
2728
requires io.hotmoka.node.local;

io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/AbstractLocalNodeImpl.java

+86-48
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import java.util.concurrent.Semaphore;
3636
import java.util.concurrent.TimeoutException;
3737
import java.util.function.BiConsumer;
38-
import java.util.logging.Level;
3938
import java.util.logging.Logger;
4039
import java.util.stream.Stream;
4140

@@ -130,10 +129,10 @@ public abstract class AbstractLocalNodeImpl<N extends AbstractLocalNodeImpl<N,C,
130129
private final ExecutorService executors;
131130

132131
/**
133-
* Cached error messages of requests that failed their {@link AbstractLocalNodeImpl#checkRequest(TransactionRequest)}.
132+
* Cached error messages of requests that failed their {@link #checkTransaction(TransactionRequest)}.
134133
* This is useful to avoid polling for the outcome of recent requests whose
135-
* {@link #checkRequest(TransactionRequest)} failed, hence never
136-
* got the chance to pass to {@link #deliverTransaction(TransactionRequest)}.
134+
* {@link #checkTransaction(TransactionRequest)} failed, hence never
135+
* got the chance to pass to delivering.
137136
*/
138137
private final LRUCache<TransactionReference, String> recentlyRejectedTransactionsMessages = new LRUCache<>(100, 1000);
139138

@@ -181,8 +180,6 @@ protected AbstractLocalNodeImpl(C config, boolean init) throws NodeException {
181180
initWorkingDirectory();
182181

183182
this.executors = Executors.newCachedThreadPool();
184-
185-
addShutdownHook();
186183
}
187184

188185
@Override
@@ -231,7 +228,7 @@ public final TransactionReference getTakamakaCode() throws NodeException, Interr
231228
return getClassTag(manifest).getJar();
232229
}
233230
catch (UnknownReferenceException e) {
234-
throw new NodeException("The manifest of the node cannot be found in the node itself", e);
231+
throw new NodeException("The manifest of the node cannot be found in the node itself: is the node initialized?", e);
235232
}
236233
}
237234
}
@@ -332,7 +329,7 @@ public final ClassTag getClassTag(StorageReference reference) throws UnknownRefe
332329
.findFirst()
333330
.orElseThrow(() -> new NodeException("Object " + reference + " has no class tag in store"));
334331
else
335-
throw new NodeException("The creation of object " + reference + " does not contain updates");
332+
throw new NodeException("The transaction that created object " + reference + " does not contain updates");
336333
}
337334
catch (StoreException e) {
338335
throw new NodeException(e);
@@ -350,7 +347,9 @@ public final Stream<Update> getState(StorageReference reference) throws UnknownR
350347
try {
351348
Stream<TransactionReference> history = store.getHistory(Objects.requireNonNull(reference));
352349
var updates = new HashSet<Update>();
353-
CheckRunnable.check(StoreException.class, () -> history.forEachOrdered(UncheckConsumer.uncheck(StoreException.class, transaction -> addUpdatesCommitted(store, reference, transaction, updates))));
350+
CheckRunnable.check(NodeException.class, () -> history.forEachOrdered(UncheckConsumer.uncheck(NodeException.class,
351+
transaction -> collectUpdates(store, reference, transaction, updates))));
352+
354353
return updates.stream();
355354
}
356355
catch (StoreException e) {
@@ -374,7 +373,7 @@ public final TransactionReference addJarStoreInitialTransaction(JarStoreInitialT
374373
@Override
375374
public final void addInitializationTransaction(InitializationTransactionRequest request) throws TransactionRejectedException, TimeoutException, InterruptedException, NodeException {
376375
try (var scope = mkScope()) {
377-
getPolledResponse(post(request)); // result unused
376+
getPolledResponse(post(request));
378377
}
379378
}
380379

@@ -492,6 +491,7 @@ public final MethodFuture postStaticMethodCallTransaction(StaticMethodCallTransa
492491
*
493492
* @return the entered store of the head
494493
* @throws NodeException if the operation could not be completed correctly
494+
* @throws InterruptedException if the current thread is interrupted while waiting for the result
495495
*/
496496
protected abstract S enterHead() throws NodeException, InterruptedException;
497497

@@ -503,20 +503,44 @@ public final MethodFuture postStaticMethodCallTransaction(StaticMethodCallTransa
503503
*/
504504
protected void exit(S store) throws NodeException {}
505505

506+
/**
507+
* Yields the executors that can be used to start tasks with this node.
508+
*
509+
* @return the executors
510+
*/
506511
protected final ExecutorService getExecutors() {
507512
return executors;
508513
}
509514

515+
/**
516+
* Yields the hasher of transactions to use with this node.
517+
*
518+
* @return the hasher of transactions
519+
*/
510520
protected final Hasher<TransactionRequest<?>> getHasher() {
511521
return hasher;
512522
}
513523

524+
/**
525+
* Takes note that this node has rejected a given transaction request.
526+
*
527+
* @param request the rejected transaction request
528+
* @param e the exception that explains why it has been rejected
529+
*/
514530
protected final void signalRejected(TransactionRequest<?> request, TransactionRejectedException e) {
515531
var reference = TransactionReferences.of(hasher.hash(request));
516532
recentlyRejectedTransactionsMessages.put(reference, e.getMessage());
517533
signalCompleted(reference);
518534
}
519535

536+
/**
537+
* Checks that the given transaction request is valid.
538+
*
539+
* @param request the request
540+
* @throws TransactionRejectedException if the request is not valid
541+
* @throws NodeException if this node is not able to perform the operation
542+
* @throws InterruptedException if the current thread is interrupted while performing the operation
543+
*/
520544
protected final void checkTransaction(TransactionRequest<?> request) throws TransactionRejectedException, NodeException, InterruptedException {
521545
S store = enterHead();
522546

@@ -539,28 +563,39 @@ protected final void checkTransaction(TransactionRequest<?> request) throws Tran
539563
* are added to the main chain of a blockchain, for each of the transactions in such blocks.
540564
*
541565
* @param reference the transaction to publish
542-
* @param store the store where {@code transaction} and its potential events can be found
566+
* @param store the store where the transaction and its potential events can be found
567+
* @throws NodeException if this node is not able to perform the operation
568+
* @throws UnknownReferenceException if {@code reference} cannot be found in {@code store}
543569
*/
544-
protected final void publish(TransactionReference reference, S store) throws NodeException {
570+
protected final void publish(TransactionReference reference, S store) throws NodeException, UnknownReferenceException {
545571
signalCompleted(reference);
546572

547573
try {
548-
if (store.getResponse(reference) instanceof TransactionResponseWithEvents trwe)
549-
CheckRunnable.check(NodeException.class, () -> trwe.getEvents().forEachOrdered(UncheckConsumer.uncheck(NodeException.class, event -> notifyEvent(event, store))));
574+
if (store.getResponse(reference) instanceof TransactionResponseWithEvents trwe) {
575+
var events = trwe.getEvents().toArray(StorageReference[]::new);
576+
for (var event: events)
577+
notifyEvent(event, store);
578+
}
550579
}
551-
catch (StoreException | UnknownReferenceException e) {
580+
catch (StoreException e) {
552581
throw new NodeException(e);
553582
}
554583
}
555584

585+
/**
586+
* Closes all the resources of this node.
587+
*
588+
* @throws NodeException if this node is not able to perform the operation
589+
*/
556590
protected void closeResources() throws NodeException {
557591
executors.shutdownNow();
558592
}
559593

560594
/**
561-
* Factory method for creating an empty store for this node, with empty cache.
595+
* Creates an empty store for this node, with empty cache.
562596
*
563-
* @return the store empty
597+
* @return the empty store
598+
* @throws NodeException if this node is not able to perform the operation
564599
*/
565600
protected abstract S mkEmptyStore() throws NodeException;
566601

@@ -569,12 +604,20 @@ protected void closeResources() throws NodeException {
569604
* for instance by adding the request to some mempool or queue of requests to be executed.
570605
*
571606
* @param request the request
607+
* @throws NodeException if this node is not able to perform the operation
608+
* @throws InterruptedException if the current thread is interrupted while performing the operation
609+
* @throws TimeoutException if the operation could not be completed in time
572610
*/
573611
protected abstract void postRequest(TransactionRequest<?> request) throws NodeException, InterruptedException, TimeoutException;
574612

613+
/**
614+
* Cleans the directory where the node's data live.
615+
*
616+
* @throws NodeException if this node is not able to perform the operation
617+
*/
575618
private void initWorkingDirectory() throws NodeException {
576619
try {
577-
deleteRecursively(config.getDir()); // cleans the directory where the node's data live
620+
deleteRecursively(config.getDir());
578621
Files.createDirectories(config.getDir());
579622
}
580623
catch (IOException e) {
@@ -600,6 +643,8 @@ private void notifyEvent(StorageReference event, S store) throws NodeException {
600643
creator = store.getCreator(event);
601644
}
602645
catch (StoreException | UnknownReferenceException | FieldNotFoundException e) {
646+
// this private method is only called on events in responses in store:
647+
// if they cannot be processed, there is a problem in the database
603648
throw new NodeException(e);
604649
}
605650

@@ -609,26 +654,28 @@ private void notifyEvent(StorageReference event, S store) throws NodeException {
609654

610655
/**
611656
* Posts the given request. It does some preliminary preparation then calls
612-
* {@link #postRequest(TransactionRequest)}, that will implement the node-specific logic of this post.
657+
* {@link #postRequest(TransactionRequest)}, that will implement the node-specific logic of posting.
613658
*
614659
* @param request the request
615660
* @return the reference of the request
616661
* @throws TransactionRejectedException if the request was already present in the store
617662
*/
618663
private TransactionReference post(TransactionRequest<?> request) throws TransactionRejectedException, NodeException, InterruptedException, TimeoutException {
619664
var reference = TransactionReferences.of(hasher.hash(request));
665+
String simpleNameOfRequest = request.getClass().getSimpleName();
666+
620667
if (request instanceof MethodCallTransactionRequest mctr)
621-
LOGGER.info(reference + ": posting (" + request.getClass().getSimpleName() + " -> " + trim(mctr.getStaticTarget().getName()) + ')');
668+
LOGGER.info(reference + ": posting (" + simpleNameOfRequest + " -> " + trim(mctr.getStaticTarget().getName()) + ')');
622669
else if (request instanceof ConstructorCallTransactionRequest cctr)
623-
LOGGER.info(reference + ": posting (" + request.getClass().getSimpleName() + " -> " + trim(cctr.getStaticTarget().getDefiningClass().getName()) + ')');
670+
LOGGER.info(reference + ": posting (" + simpleNameOfRequest + " -> " + trim(cctr.getStaticTarget().getDefiningClass().getName()) + ')');
624671
else
625-
LOGGER.info(reference + ": posting (" + request.getClass().getSimpleName() + ')');
672+
LOGGER.info(reference + ": posting (" + simpleNameOfRequest + ')');
626673

627674
S store = enterHead();
628675

629676
try {
630677
store.getResponse(reference);
631-
// if the response is found, then no exception is thrown above and the request was repeated
678+
// if the response is found, then no exception is thrown above, which means that the request was repeated
632679
throw new TransactionRejectedException("Repeated request " + reference, store.getConfig());
633680
}
634681
catch (StoreException e) {
@@ -652,33 +699,38 @@ private static String trim(String s) {
652699
}
653700

654701
/**
655-
* Adds, to the given set, the updates of the fields of the object at the given reference,
702+
* Collects, into the given set, the updates of the fields of an object
656703
* occurred during the execution of a given transaction.
657704
*
658-
* @param object the reference of the object
659-
* @param referenceInHistory the reference to the transaction
705+
* @param store the store of this node
706+
* @param object the reference to the object
707+
* @param reference the reference to the transaction
660708
* @param updates the set where they must be added
661-
* @throws StoreException
709+
* @throws NodeException if this node is misbehaving
662710
*/
663-
private void addUpdatesCommitted(S store, StorageReference object, TransactionReference referenceInHistory, Set<Update> updates) throws StoreException {
711+
private void collectUpdates(S store, StorageReference object, TransactionReference reference, Set<Update> updates) throws NodeException {
664712
try {
665-
if (store.getResponse(referenceInHistory) instanceof TransactionResponseWithUpdates trwu)
713+
if (store.getResponse(reference) instanceof TransactionResponseWithUpdates trwu)
666714
trwu.getUpdates()
667715
.filter(update -> update.getObject().equals(object) && updates.stream().noneMatch(update::sameProperty))
668716
.forEach(updates::add);
669717
else
670-
throw new StoreException("Reference " + referenceInHistory + " is part of the histories but did not generate updates");
718+
throw new NodeException("Reference " + reference + " is part of the histories but did not generate updates");
671719
}
672720
catch (UnknownReferenceException e) {
673-
throw new StoreException("Reference " + referenceInHistory + " is part of the histories but is not in the store");
721+
throw new NodeException("Reference " + reference + " is part of the histories but is not in the store");
722+
}
723+
catch (StoreException e) {
724+
throw new NodeException(e);
674725
}
675726
}
676727

677728
/**
678-
* Creates a semaphore for those who will wait for the result of the given request.
729+
* Creates a semaphore for those who will wait for the result of a request.
679730
*
680-
* @param reference the reference of the transaction for the request
681-
* @throws TransactionRejectedException
731+
* @param reference the reference of the transaction of the request
732+
* @throws TransactionRejectedException if there is already a semaphore for {@code reference}, which
733+
* means that the request is repeated
682734
*/
683735
private void createSemaphore(S store, TransactionReference reference) throws TransactionRejectedException {
684736
if (semaphores.putIfAbsent(reference, new Semaphore(0)) != null)
@@ -698,18 +750,4 @@ private static void deleteRecursively(Path dir) throws IOException {
698750
.map(Path::toFile)
699751
.forEach(File::delete);
700752
}
701-
702-
/**
703-
* Adds a shutdown hook that shuts down the blockchain orderly if the JVM terminates.
704-
*/
705-
private void addShutdownHook() { // TODO: do we really need it? It seems that it is never called
706-
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
707-
try {
708-
close();
709-
}
710-
catch (NodeException e) {
711-
LOGGER.log(Level.SEVERE, "The shutdown hook of the node failed", e);
712-
}
713-
}));
714-
}
715753
}

io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/AbstractStoreTransformationImpl.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import io.hotmoka.node.api.transactions.TransactionReference;
6161
import io.hotmoka.node.api.updates.Update;
6262
import io.hotmoka.node.api.values.StorageReference;
63+
import io.hotmoka.node.local.api.EngineClassLoader;
6364
import io.hotmoka.node.local.api.FieldNotFoundException;
6465
import io.hotmoka.node.local.api.LocalNodeConfig;
6566
import io.hotmoka.node.local.api.ResponseBuilder;
@@ -336,7 +337,7 @@ protected final BigInteger getGasConsumed() {
336337
* @param classLoader the class loader of the transaction that computed {@code response}
337338
* @throws InterruptedException if the current thread is interrupted before completing the operation
338339
*/
339-
protected void updateCaches(TransactionResponse response, TakamakaClassLoader classLoader) throws StoreException, InterruptedException {
340+
protected void updateCaches(TransactionResponse response, EngineClassLoader classLoader) throws StoreException, InterruptedException {
340341
if (manifestMightHaveChanged(response)) {
341342
cache = cache.setValidators(extractValidators());
342343
LOGGER.info("the validators cache has been updated since it might have changed");

io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/Reverification.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public class Reverification {
6464
private final ConcurrentMap<TransactionReference, TransactionResponse> reverified = new ConcurrentHashMap<>();
6565

6666
/**
67-
* The execution environment whete the reverification is performed.
67+
* The execution environment where the reverification is performed.
6868
*/
6969
private final ExecutionEnvironment environment;
7070

0 commit comments

Comments
 (0)