diff --git a/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/DiskNodeConfigImpl.java b/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/DiskNodeConfigImpl.java index 17c462bc9..0388a3421 100644 --- a/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/DiskNodeConfigImpl.java +++ b/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/DiskNodeConfigImpl.java @@ -86,7 +86,7 @@ public static class DiskNodeConfigBuilderImpl extends AbstractLocalNodeConfigBui /** * The number of transactions that fit inside a block. */ - private long transactionsPerBlock = 5; + private long transactionsPerBlock = 10; /** * Creates a builder with default values for the properties. diff --git a/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/DiskNodeImpl.java b/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/DiskNodeImpl.java index 08982ded3..1bf50ccb7 100644 --- a/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/DiskNodeImpl.java +++ b/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/DiskNodeImpl.java @@ -84,7 +84,7 @@ public DiskNodeConfig getLocalConfig() { @Override protected DiskStore mkStore() { - return new DiskStore(getLocalConfig().getDir(), getLocalConfig().getTransactionsPerBlock()); + return new DiskStore(getLocalConfig().getDir()); } @Override @@ -93,7 +93,7 @@ protected long getNow() { } @Override - protected StoreTransaction getTransaction() { + public StoreTransaction getTransaction() { return transaction; } diff --git a/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/DiskStore.java b/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/DiskStore.java index 98952b5e1..59796c96b 100644 --- a/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/DiskStore.java +++ b/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/DiskStore.java @@ -68,43 +68,26 @@ class DiskStore extends AbstractStore { */ private final AtomicReference manifest; - /** - * The number of transactions added to the store. This is used to associate - * each transaction to its progressive number. - */ - private final AtomicInteger transactionsCount; - - /** - * A map from the transactions added to the store to their progressive number. - * This is needed in order to give a nice presentation of transactions, inside a - * directory for its block. - */ - private final ConcurrentMap progressive; + private final ConcurrentMap paths = new ConcurrentHashMap<>(); /** * The path where the database of the store gets created. */ private final Path dir; - /** - * The number of transactions that fit inside a block. - */ - private final long transactionsPerBlock; + private final AtomicInteger blockNumber; /** * Creates a state for a node. * * @param dir the path where the database of the store gets created - * @param transactionsPerBlock the number of transactions that fit inside a block */ - DiskStore(Path dir, long transactionsPerBlock) { + DiskStore(Path dir) { this.dir = dir; - this.transactionsPerBlock = transactionsPerBlock; this.histories = new ConcurrentHashMap<>(); this.errors = new ConcurrentHashMap<>(); - this.progressive = new ConcurrentHashMap<>(); this.manifest = new AtomicReference<>(); - this.transactionsCount = new AtomicInteger(); + this.blockNumber = new AtomicInteger(0); } /** @@ -116,18 +99,20 @@ private DiskStore(DiskStore toClone) { super(toClone); this.dir = toClone.dir; - this.transactionsPerBlock = toClone.transactionsPerBlock; this.histories = toClone.histories; this.errors = toClone.errors; - this.progressive = toClone.progressive; this.manifest = toClone.manifest; - this.transactionsCount = toClone.transactionsCount; + this.blockNumber = toClone.blockNumber; } @Override public Optional getResponse(TransactionReference reference) { try { - Path response = getPathFor(reference, "response"); + Path path = paths.get(reference); + if (path == null) + return Optional.empty(); + + Path response = path.resolve("response"); try (var context = NodeUnmarshallingContexts.of(Files.newInputStream(response))) { return Optional.of(TransactionResponses.from(context)); } @@ -137,11 +122,6 @@ public Optional getResponse(TransactionReference reference) } } - @Override - public Optional getResponseUncommitted(TransactionReference reference) { - return getResponse(reference); - } - @Override public Optional getError(TransactionReference reference) { return Optional.ofNullable(errors.get(reference)); @@ -153,25 +133,19 @@ public Stream getHistory(StorageReference object) { return history == null ? Stream.empty() : Stream.of(history); } - @Override - public Stream getHistoryUncommitted(StorageReference object) { - return getHistory(object); - } - @Override public Optional getManifest() { return Optional.ofNullable(manifest.get()); } - @Override - public Optional getManifestUncommitted() { - return getManifest(); - } - @Override public Optional> getRequest(TransactionReference reference) { try { - Path response = getPathFor(reference, "request"); + Path path = paths.get(reference); + if (path == null) + return Optional.empty(); + + Path response = path.resolve("request"); try (var context = NodeUnmarshallingContexts.of(Files.newInputStream(response))) { return Optional.of(TransactionRequests.from(context)); } @@ -196,65 +170,52 @@ protected DiskStore mkClone() { return new DiskStore(this); } - @Override - protected DiskStore setRequest(TransactionReference reference, TransactionRequest request) throws StoreException { + protected void setRequest(int progressive, TransactionReference reference, TransactionRequest request) throws StoreException { try { - progressive.computeIfAbsent(reference, _reference -> transactionsCount.getAndIncrement()); - Path requestPath = getPathFor(reference, "request"); + Path requestPath = getPathFor(progressive, reference, "request"); Path parent = requestPath.getParent(); + paths.put(reference, parent); ensureDeleted(parent); Files.createDirectories(parent); - Files.writeString(getPathFor(reference, "request.txt"), request.toString(), StandardCharsets.UTF_8); + Files.writeString(getPathFor(progressive, reference, "request.txt"), request.toString(), StandardCharsets.UTF_8); try (var context = NodeMarshallingContexts.of(Files.newOutputStream(requestPath))) { request.into(context); } - - return this; } catch (IOException e) { throw new StoreException(e); } } - @Override - protected DiskStore setResponse(TransactionReference reference, TransactionResponse response) throws StoreException { + protected void setResponse(int progressive, TransactionReference reference, TransactionResponse response) throws StoreException { try { - progressive.computeIfAbsent(reference, _reference -> transactionsCount.getAndIncrement()); - Path responsePath = getPathFor(reference, "response"); + Path responsePath = getPathFor(progressive, reference, "response"); Path parent = responsePath.getParent(); Files.createDirectories(parent); - Files.writeString(getPathFor(reference, "response.txt"), response.toString(), StandardCharsets.UTF_8); + Files.writeString(getPathFor(progressive, reference, "response.txt"), response.toString(), StandardCharsets.UTF_8); try (var context = NodeMarshallingContexts.of(Files.newOutputStream(responsePath))) { response.into(context); } - - return this; } catch (IOException e) { throw new StoreException(e); } } - @Override - protected DiskStore setError(TransactionReference reference, String error) { + protected void setError(TransactionReference reference, String error) { errors.put(reference, error); - return this; } - @Override - protected DiskStore setHistory(StorageReference object, Stream history) { + protected void setHistory(StorageReference object, Stream history) { histories.put(object, history.toArray(TransactionReference[]::new)); - return this; } - @Override - protected DiskStore setManifest(StorageReference manifest) { + protected void setManifest(StorageReference manifest) { this.manifest.set(manifest); - return this; } /** @@ -265,12 +226,8 @@ protected DiskStore setManifest(StorageReference manifest) { * @return the resulting path * @throws FileNotFoundException if the reference is unknown */ - private Path getPathFor(TransactionReference reference, String name) throws FileNotFoundException { - Integer progressive = this.progressive.get(reference); - if (progressive == null) - throw new FileNotFoundException("Unknown transaction reference " + reference); - - return dir.resolve("b" + progressive / transactionsPerBlock).resolve(progressive % transactionsPerBlock + "-" + reference).resolve(name); + private Path getPathFor(int progressive, TransactionReference reference, String name) throws FileNotFoundException { + return dir.resolve("b" + blockNumber.get()).resolve(progressive + "-" + reference).resolve(name); } /** @@ -286,4 +243,8 @@ private static void ensureDeleted(Path dir) throws IOException { .map(Path::toFile) .forEach(File::delete); } + + void increaseBlockNumber() { + blockNumber.getAndIncrement(); + } } \ No newline at end of file diff --git a/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/DiskStoreTransaction.java b/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/DiskStoreTransaction.java index a4f893729..8dfe092e1 100644 --- a/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/DiskStoreTransaction.java +++ b/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/DiskStoreTransaction.java @@ -1,6 +1,9 @@ package io.hotmoka.node.disk.internal; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; import io.hotmoka.node.api.requests.TransactionRequest; @@ -12,52 +15,110 @@ public class DiskStoreTransaction extends AbstractStoreTransaction { + private final DiskStore store; + + private final ConcurrentMap> requests = new ConcurrentHashMap<>(); + private final ConcurrentMap responses = new ConcurrentHashMap<>(); + + /** + * The histories of the objects created in blockchain. In a real implementation, this must + * be stored in a persistent state. + */ + private final ConcurrentMap histories = new ConcurrentHashMap<>(); + + /** + * The errors generated by each transaction (if any). In a real implementation, this must + * be stored in a persistent memory such as a blockchain. + */ + private final ConcurrentMap errors = new ConcurrentHashMap<>(); + + /** + * The storage reference of the manifest stored inside the node, if any. + */ + private final AtomicReference manifest = new AtomicReference<>(); + public DiskStoreTransaction(DiskStore store, Object lock) { - super(store, lock); + super(lock); + + this.store = store; } @Override - public Optional getResponse(TransactionReference reference) { - return store.getResponseUncommitted(reference); + public Optional getResponseUncommitted(TransactionReference reference) { + var uncommittedResponse = responses.get(reference); + if (uncommittedResponse != null) + return Optional.of(uncommittedResponse); + else + return store.getResponse(reference); } @Override - public Stream getHistory(StorageReference object) throws StoreException { - return store.getHistoryUncommitted(object); + public Stream getHistoryUncommitted(StorageReference object) throws StoreException { + var uncommittedHistory = histories.get(object); + if (uncommittedHistory != null) + return Stream.of(uncommittedHistory); + else + return store.getHistory(object); } @Override - public Optional getManifest() throws StoreException { - return store.getManifestUncommitted(); + public Optional getManifestUncommitted() { + var uncommittedManifest = manifest.get(); + if (uncommittedManifest != null) + return Optional.of(uncommittedManifest); + else + return store.getManifest(); } @Override - public DiskStore commit() { + public DiskStore commit() throws StoreException { + // we report all the updates occurred during this transaction into the store + var manifest = this.manifest.get(); + if (manifest != null) + store.setManifest(manifest); + + for (var entry: errors.entrySet()) + store.setError(entry.getKey(), entry.getValue()); + + for (var entry: histories.entrySet()) + store.setHistory(entry.getKey(), Stream.of(entry.getValue())); + + int progressive = 0; + for (var entry: requests.entrySet()) + store.setRequest(progressive++, entry.getKey(), entry.getValue()); + + progressive = 0; + for (var entry: responses.entrySet()) + store.setResponse(progressive++, entry.getKey(), entry.getValue()); + + if (progressive > 0) + store.increaseBlockNumber(); + return store; } @Override - protected void setRequest(TransactionReference reference, TransactionRequest request) throws StoreException { - store = store.setRequest(reference, request); + protected void setRequest(TransactionReference reference, TransactionRequest request) { + requests.put(reference, request); } @Override - protected void setResponse(TransactionReference reference, TransactionResponse response) throws StoreException { - store = store.setResponse(reference, response); + protected void setResponse(TransactionReference reference, TransactionResponse response) { + responses.put(reference, response); } @Override - protected void setError(TransactionReference reference, String error) throws StoreException { - store = store.setError(reference, error); + protected void setError(TransactionReference reference, String error) { + errors.put(reference, error); } @Override protected void setHistory(StorageReference object, Stream history) { - store = store.setHistory(object, history); + histories.put(object, history.toArray(TransactionReference[]::new)); } @Override - protected void setManifest(StorageReference manifest) throws StoreException { - store = store.setManifest(manifest); + protected void setManifest(StorageReference manifest) { + this.manifest.set(manifest); } } \ No newline at end of file diff --git a/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/Mempool.java b/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/Mempool.java index 07ccdbf43..223b2c5b3 100644 --- a/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/Mempool.java +++ b/io-hotmoka-node-disk/src/main/java/io/hotmoka/node/disk/internal/Mempool.java @@ -18,6 +18,7 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; @@ -128,25 +129,34 @@ private void deliver() { while (!Thread.currentThread().isInterrupted()) { try { - TransactionRequest current = checkedMempool.take(); - - try { + TransactionRequest current = checkedMempool.poll(10, TimeUnit.MILLISECONDS); + if (current == null) { + if (counter > 0) + node.rewardValidators("", ""); + transaction.commit(); + transaction = node.getStore().beginTransaction(); + node.setNow(transaction); + counter = 0; + } + else { node.deliverTransaction(current); - counter = (counter + 1) % transactionsPerBlock; + counter = (counter + 1) % transactionsPerBlock; // TODO: transactionsPerBlock should be int // the last transaction of a block is for rewarding the validators and updating the gas price - if (counter == transactionsPerBlock - 1 && node.rewardValidators("", "")) { - counter = 0L; + if (counter == transactionsPerBlock - 1) { + node.rewardValidators("", ""); + transaction.commit(); transaction = node.getStore().beginTransaction(); node.setNow(transaction); + counter = 0; } } - catch (Throwable t) { - logger.log(Level.WARNING, "Failed to deliver transaction request", t); - } } catch (InterruptedException e) { return; } + catch (Throwable t) { + logger.log(Level.WARNING, "Failed to deliver transaction request", t); + } } } } \ No newline at end of file diff --git a/io-hotmoka-node-local-api/src/main/java/io/hotmoka/node/local/api/LocalNodeConfig.java b/io-hotmoka-node-local-api/src/main/java/io/hotmoka/node/local/api/LocalNodeConfig.java index aa7e9d226..b3ebf7500 100644 --- a/io-hotmoka-node-local-api/src/main/java/io/hotmoka/node/local/api/LocalNodeConfig.java +++ b/io-hotmoka-node-local-api/src/main/java/io/hotmoka/node/local/api/LocalNodeConfig.java @@ -66,10 +66,10 @@ public interface LocalNodeConfig, B extends Local int getRequestCacheSize(); /** - * Yields the size of the cache for the {@link io.hotmoka.node.api.Node#getResponse(TransactionReference)} method. + * Yields the size of the cache for the {@link io.hotmoka.node.api.Node#getResponseUncommited(TransactionReference)} method. * It defaults to 1,000. * - * @return the size of the cache for the {@link io.hotmoka.node.api.Node#getResponse(TransactionReference)} method + * @return the size of the cache for the {@link io.hotmoka.node.api.Node#getResponseUncommited(TransactionReference)} method */ int getResponseCacheSize(); diff --git a/io-hotmoka-node-local-api/src/main/java/io/hotmoka/node/local/api/LocalNodeConfigBuilder.java b/io-hotmoka-node-local-api/src/main/java/io/hotmoka/node/local/api/LocalNodeConfigBuilder.java index e2087017d..3b48a11cf 100644 --- a/io-hotmoka-node-local-api/src/main/java/io/hotmoka/node/local/api/LocalNodeConfigBuilder.java +++ b/io-hotmoka-node-local-api/src/main/java/io/hotmoka/node/local/api/LocalNodeConfigBuilder.java @@ -77,7 +77,7 @@ public interface LocalNodeConfigBuilder, B extend B setRequestCacheSize(int requestCacheSize); /** - * Sets size of the cache for the {@link io.hotmoka.node.api.Node#getResponse(TransactionReference)} method. + * Sets size of the cache for the {@link io.hotmoka.node.api.Node#getResponseUncommited(TransactionReference)} method. * It defaults to 1,000. * * @param responseCacheSize the cache size diff --git a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/AbstractLocalNodeImpl.java b/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/AbstractLocalNodeImpl.java index c17c79e56..5ab9fc678 100644 --- a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/AbstractLocalNodeImpl.java +++ b/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/AbstractLocalNodeImpl.java @@ -343,7 +343,7 @@ private AbstractLocalNodeImpl(C config, ConsensusConfig consensus, boolean * * @return the currently executing transaction */ - protected abstract StoreTransaction getTransaction(); + public abstract StoreTransaction getTransaction(); /** * Determines if this node has not been closed yet. @@ -683,7 +683,8 @@ protected final TransactionResponse deliverTransaction(TransactionRequest req synchronized (deliverTransactionLock) { ResponseBuilder responseBuilder = responseBuilderFor(reference, request); response = responseBuilder.getResponse(); - store = store.push(reference, request, response); + var transaction = getTransaction(); + transaction.push(reference, request, response); responseBuilder.replaceReverifiedResponses(); scheduleForNotificationOfEvents(response); takeNoteForNextReward(request, response); @@ -694,18 +695,21 @@ protected final TransactionResponse deliverTransaction(TransactionRequest req return response; } catch (TransactionRejectedException e) { - store = store.push(reference, request, trimmedMessage(e)); + var transaction = getTransaction(); + transaction.push(reference, request, trimmedMessage(e)); LOGGER.info(reference + ": delivering failed: " + trimmedMessage(e)); LOGGER.log(Level.INFO, "transaction rejected", e); throw e; } catch (ClassNotFoundException | NodeException | UnknownReferenceException e) { - store = store.push(reference, request, trimmedMessage(e)); + var transaction = getTransaction(); + transaction.push(reference, request, trimmedMessage(e)); LOGGER.log(Level.SEVERE, reference + ": delivering failed with unexpected exception", e); throw new RuntimeException(e); } catch (RuntimeException e) { - store = store.push(reference, request, trimmedMessage(e)); + var transaction = getTransaction(); + transaction.push(reference, request, trimmedMessage(e)); LOGGER.log(Level.WARNING, reference + ": delivering failed with unexpected exception", e); throw e; } @@ -739,7 +743,7 @@ protected final boolean rewardValidators(String behaving, String misbehaving) { return false; try { - Optional manifest = store.getManifestUncommitted(); + Optional manifest = getTransaction().getManifestUncommitted(); if (manifest.isPresent()) { // we use the manifest as caller, since it is an externally-owned account StorageReference caller = manifest.get(); @@ -925,6 +929,53 @@ protected void setStore(Store store) { */ protected abstract void scheduleForNotificationOfEvents(TransactionResponseWithEvents response); + /** + * 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 + */ + private Optional getResponseUncommitted(TransactionReference reference) throws StoreException { + var transaction = getTransaction(); + if (transaction != null) + return transaction.getResponseUncommitted(reference); + else + return store.getResponse(reference); + } + + /** + * Yields the history of the given object, that is, the references to the transactions + * that can be used to reconstruct the current values of its fields. + * This considers also updates inside this transaction, that have not yet been committed. + * + * @param object the reference of the object + * @return the history. Yields an empty stream if there is no history for {@code object} + * @throws StoreException if the store is not able to perform the operation + */ + private Stream getHistoryUncommitted(StorageReference object) throws StoreException { + var transaction = getTransaction(); + if (transaction != null) + return transaction.getHistoryUncommitted(object); + else + return store.getHistory(object); + } + + /** + * Yields the manifest installed when the node is initialized. + * This considers also updates inside this transaction, that have not yet been committed. + * + * @return the manifest + * @throws StoreException if the store is not able to complete the operation correctly + */ + private Optional getManifestUncommitted() throws StoreException { + var transaction = getTransaction(); + if (transaction != null) + return transaction.getManifestUncommitted(); + else + return store.getManifest(); + } + /** * Runs a callable and wraps any exception into an {@link TransactionRejectedException}, * if it is not a {@link TransactionException} nor a {@link CodeExecutionException}. @@ -1151,15 +1202,46 @@ public Store getStore() { return store; } - public void setStore(Store store) { - AbstractLocalNodeImpl.this.store = (S) store; // TODO - } - @Override public StoreUtility getStoreUtilities() { return storeUtilities; } + /** + * 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 + */ + public Optional getResponseUncommitted(TransactionReference reference) throws StoreException { + return AbstractLocalNodeImpl.this.getResponseUncommitted(reference); + } + + /** + * Yields the history of the given object, that is, the references to the transactions + * that can be used to reconstruct the current values of its fields. + * This considers also updates inside this transaction, that have not yet been committed. + * + * @param object the reference of the object + * @return the history. Yields an empty stream if there is no history for {@code object} + * @throws StoreException if the store is not able to perform the operation + */ + public Stream getHistoryUncommitted(StorageReference object) throws StoreException { + return AbstractLocalNodeImpl.this.getHistoryUncommitted(object); + } + + /** + * Yields the manifest installed when the node is initialized. + * This considers also updates inside this transaction, that have not yet been committed. + * + * @return the manifest + * @throws StoreException if the store is not able to complete the operation correctly + */ + public Optional getManifestUncommitted() throws StoreException { + return AbstractLocalNodeImpl.this.getManifestUncommitted(); + } + @Override public TransactionRequest getRequest(TransactionReference reference) throws UnknownReferenceException, NodeException { return AbstractLocalNodeImpl.this.getRequest(reference); @@ -1194,5 +1276,10 @@ public void submit(Runnable task) { public long getNow() { return AbstractLocalNodeImpl.this.getNow(); } + + @Override + public StoreTransaction getTransaction() { + return AbstractLocalNodeImpl.this.getTransaction(); + } } } \ No newline at end of file diff --git a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/LocalNodeConfigImpl.java b/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/LocalNodeConfigImpl.java index a321126a1..3293122d7 100644 --- a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/LocalNodeConfigImpl.java +++ b/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/LocalNodeConfigImpl.java @@ -65,7 +65,7 @@ public abstract class LocalNodeConfigImpl, B exte public final int requestCacheSize; /** - * The size of the cache for the {@link io.hotmoka.node.api.Node#getResponse(TransactionReference)} method. + * The size of the cache for the {@link io.hotmoka.node.api.Node#getResponseUncommited(TransactionReference)} method. * It defaults to 1,000. */ public final int responseCacheSize; diff --git a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/NodeCachesImpl.java b/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/NodeCachesImpl.java index 044f11b60..032148560 100644 --- a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/NodeCachesImpl.java +++ b/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/NodeCachesImpl.java @@ -40,6 +40,7 @@ import io.hotmoka.crypto.Base64ConversionException; import io.hotmoka.crypto.SignatureAlgorithms; import io.hotmoka.crypto.api.SignatureAlgorithm; +import io.hotmoka.exceptions.UncheckSupplier; import io.hotmoka.node.MethodSignatures; import io.hotmoka.node.StorageTypes; import io.hotmoka.node.TransactionRequests; @@ -192,7 +193,7 @@ public final void recomputeConsensus() { StorageReference validators = getValidators().get(); StorageReference versions = getVersions().get(); TransactionReference takamakaCode = node.getStoreUtilities().getTakamakaCodeUncommitted().get(); - StorageReference manifest = node.getStore().getManifestUncommitted().get(); + StorageReference manifest = node.getManifestUncommitted().get(); String genesisTime = ((StringValue) node.runInstanceMethodCallTransaction(TransactionRequests.instanceViewMethodCall (manifest, _100_000, takamakaCode, MethodSignatures.GET_GENESIS_TIME, manifest)) @@ -345,7 +346,7 @@ public final Optional getResponse(TransactionReference refe @Override public final Optional getResponseUncommitted(TransactionReference reference) { - return getResponse(reference).or(() -> node.getStore().getResponseUncommitted(reference)); + return getResponse(reference).or(UncheckSupplier.uncheck(() -> node.getResponseUncommitted(reference))); // TODO: recheck } @Override @@ -460,7 +461,7 @@ private PublicKey getPublicKey(StorageReference reference, SignatureAlgorithm si private void recomputeGasPrice() { try { - Optional manifest = node.getStore().getManifestUncommitted(); + Optional manifest = node.getManifestUncommitted(); if (manifest.isPresent()) gasPrice = ((BigIntegerValue) node.runInstanceMethodCallTransaction(TransactionRequests.instanceViewMethodCall (manifest.get(), _100_000, node.getStoreUtilities().getTakamakaCodeUncommitted().get(), @@ -474,7 +475,7 @@ MethodSignatures.GET_GAS_PRICE, getGasStation().get())) private void recomputeInflation() { try { - Optional manifest = node.getStore().getManifestUncommitted(); + Optional manifest = node.getManifestUncommitted(); if (manifest.isPresent()) inflation = ((LongValue) node.runInstanceMethodCallTransaction(TransactionRequests.instanceViewMethodCall (manifest.get(), _100_000, node.getStoreUtilities().getTakamakaCodeUncommitted().get(), @@ -504,7 +505,7 @@ private boolean consensusParametersMightHaveChanged(TransactionResponse response try { if (isInitializedUncommitted() && response instanceof TransactionResponseWithEvents) { Stream events = ((TransactionResponseWithEvents) response).getEvents(); - StorageReference manifest = node.getStore().getManifestUncommitted().get(); + StorageReference manifest = node.getManifestUncommitted().get(); StorageReference gasStation = getGasStation().get(); StorageReference versions = getVersions().get(); StorageReference validators = getValidators().get(); @@ -531,7 +532,7 @@ private boolean consensusParametersMightHaveChanged(TransactionResponse response * @throws StoreException */ private boolean isInitializedUncommitted() throws StoreException { - return node.getStore().getManifestUncommitted().isPresent(); + return node.getManifestUncommitted().isPresent(); } private boolean isConsensusUpdateEvent(StorageReference event, EngineClassLoader classLoader) throws ClassNotFoundException { diff --git a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/NodeInternal.java b/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/NodeInternal.java index 41806eb9f..e6950b074 100644 --- a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/NodeInternal.java +++ b/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/NodeInternal.java @@ -19,6 +19,7 @@ import java.util.Optional; import java.util.concurrent.Callable; import java.util.concurrent.Future; +import java.util.stream.Stream; import io.hotmoka.instrumentation.api.GasCostModel; import io.hotmoka.node.api.CodeExecutionException; @@ -37,6 +38,8 @@ import io.hotmoka.node.local.api.NodeCache; import io.hotmoka.node.local.api.StoreUtility; import io.hotmoka.stores.Store; +import io.hotmoka.stores.StoreException; +import io.hotmoka.stores.StoreTransaction; /** * The methods of a Hotmoka node that are used inside the internal @@ -74,7 +77,7 @@ public interface NodeInternal { */ Store getStore(); - void setStore(Store store); + StoreTransaction getTransaction(); /** * Yields an object that provides methods for reconstructing data from the store of this node. @@ -134,6 +137,34 @@ public interface NodeInternal { */ ClassTag getClassTag(StorageReference object) throws UnknownReferenceException, NodeException; + /** + * 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 + */ + Optional getResponseUncommitted(TransactionReference reference) throws StoreException; + + /** + * Yields the history of the given object, that is, the references to the transactions + * that can be used to reconstruct the current values of its fields. + * This considers also updates inside this transaction, that have not yet been committed. + * + * @param object the reference of the object + * @return the history. Yields an empty stream if there is no history for {@code object} + * @throws StoreException if the store is not able to perform the operation + */ + Stream getHistoryUncommitted(StorageReference object) throws StoreException; + + /** + * Yields the manifest installed when the node is initialized. + * This considers also updates inside this transaction, that have not yet been committed. + * + * @return the manifest + * @throws StoreException if the store is not able to complete the operation correctly + */ + Optional getManifestUncommitted() throws StoreException; /** * Runs an instance {@code @@View} method of an object already in this node's store. * The node's store is not expanded, since the execution of the method has no side-effects. diff --git a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/Reverification.java b/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/Reverification.java index a22990a74..b3ed6f0b2 100644 --- a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/Reverification.java +++ b/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/Reverification.java @@ -113,7 +113,8 @@ public void replace() throws NodeException { var reference = entry.getKey(); try { - node.setStore(node.getStore().replace(reference, node.getRequest(reference), entry.getValue())); + var transaction = node.getTransaction(); + transaction.replace(reference, node.getRequest(reference), entry.getValue()); } catch (StoreException | UnknownReferenceException e) { throw new NodeException(e); diff --git a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/StoreUtilityImpl.java b/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/StoreUtilityImpl.java index 920d9a357..79d174bf8 100644 --- a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/StoreUtilityImpl.java +++ b/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/StoreUtilityImpl.java @@ -87,14 +87,14 @@ public StoreUtilityImpl(NodeInternal node, Store store) { @Override public Optional getTakamakaCodeUncommitted() throws StoreException { - return getStore().getManifestUncommitted() + return node.getManifestUncommitted() .map(this::getClassTagUncommitted) .map(ClassTag::getJar); } @Override public Optional getManifestUncommitted() throws StoreException { - return getStore().getManifestUncommitted(); + return node.getManifestUncommitted(); } @Override @@ -187,7 +187,7 @@ public Stream getEagerFieldsUncommitted(StorageReference object) Set fieldsAlreadySeen = new HashSet<>(); NodeCache caches = node.getCaches(); - return getStore().getHistoryUncommitted(object) + return node.getHistoryUncommitted(object) .flatMap(transaction -> enforceHasUpdates(caches.getResponseUncommitted(transaction).get()).getUpdates()) .filter(update -> update.isEager() && update instanceof UpdateOfField && update.getObject().equals(object) && fieldsAlreadySeen.add(((UpdateOfField) update).getField())) @@ -196,7 +196,7 @@ public Stream getEagerFieldsUncommitted(StorageReference object) @Override public Optional getLastUpdateToFieldUncommitted(StorageReference object, FieldSignature field) throws StoreException { - return getStore().getHistoryUncommitted(object) + return node.getHistoryUncommitted(object) .map(transaction -> getLastUpdateUncommitted(object, field, transaction)) .filter(Optional::isPresent) .map(Optional::get) diff --git a/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintApplication.java b/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintApplication.java index 22ba61d8c..92ff36200 100644 --- a/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintApplication.java +++ b/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintApplication.java @@ -33,6 +33,7 @@ import io.hotmoka.node.api.NodeException; import io.hotmoka.node.api.TransactionException; import io.hotmoka.node.api.TransactionRejectedException; +import io.hotmoka.stores.StoreException; import io.hotmoka.stores.StoreTransaction; import io.hotmoka.tendermint.abci.ABCI; import tendermint.abci.Types.Evidence; @@ -224,8 +225,14 @@ protected ResponseBeginBlock beginBlock(RequestBeginBlock request) { String behaving = spaceSeparatedSequenceOfBehavingValidatorsAddresses(request); String misbehaving = spaceSeparatedSequenceOfMisbehavingValidatorsAddresses(request); long now = timeNow(request); - - transaction = node.getStore().beginTransaction(); + + try { + transaction = node.getStore().beginTransaction(); + } + catch (StoreException e) { + throw new RuntimeException(e); // TODO + } + node.setNow(transaction, now); logger.info("validators reward: behaving: " + behaving + ", misbehaving: " + misbehaving); node.rewardValidators(behaving, misbehaving); @@ -283,11 +290,16 @@ protected ResponseEndBlock endBlock(RequestEndBlock request) { } @Override - protected ResponseCommit commit(RequestCommit request) { - TendermintStore store = node.getStore(); - node.commitTransactionAndCheckout(); - // hash of the store, used for consensus - byte[] hash = store.getHash(); + protected ResponseCommit commit(RequestCommit request) { + try { + node.commitTransactionAndCheckout(); + } + catch (StoreException e) { + throw new RuntimeException(e); // TODO + } + + // hash of the store, used for consensus + byte[] hash = node.getStore().getHash(); logger.info("Committed state with hash = " + Hex.toHexString(hash).toUpperCase()); return ResponseCommit.newBuilder() .setData(ByteString.copyFrom(hash)) diff --git a/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintNodeImpl.java b/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintNodeImpl.java index a08663878..5c1d1f102 100644 --- a/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintNodeImpl.java +++ b/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintNodeImpl.java @@ -169,7 +169,7 @@ public TendermintNodeImpl(TendermintNodeConfig config) throws IOException { try { this.isWindows = System.getProperty("os.name").startsWith("Windows"); - TendermintConfigFile tendermintConfigFile = new TendermintConfigFile(config); + var tendermintConfigFile = new TendermintConfigFile(config); this.abci = new Server(tendermintConfigFile.abciPort, new TendermintApplication(new TendermintBlockchainInternalImpl())); this.abci.start(); LOGGER.info("ABCI started at port " + tendermintConfigFile.abciPort); @@ -265,8 +265,9 @@ protected void scheduleForNotificationOfEvents(TransactionResponseWithEvents res */ private final Set responsesWithEventsToNotify = new HashSet<>(); - private void commitTransactionAndCheckout() { - setStore(store.endTransaction()); + private void commitTransactionAndCheckout() throws StoreException { + setStore(transaction.commit()); + store.moveRootBranchToThis(); responsesWithEventsToNotify.forEach(this::notifyEventsOf); responsesWithEventsToNotify.clear(); } @@ -399,7 +400,7 @@ public Optional getTendermintValidatorsInStore() throws T } @Override - public void commitTransactionAndCheckout() { + public void commitTransactionAndCheckout() throws StoreException { TendermintNodeImpl.this.commitTransactionAndCheckout(); } @@ -568,7 +569,7 @@ private void initWorkingDirectoryOfTendermintProcess(TendermintNodeConfig config } @Override - protected StoreTransaction getTransaction() { + public StoreTransaction getTransaction() { return transaction; } } \ No newline at end of file diff --git a/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintNodeInternal.java b/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintNodeInternal.java index f6a8784df..a355da30e 100644 --- a/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintNodeInternal.java +++ b/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintNodeInternal.java @@ -25,6 +25,7 @@ import io.hotmoka.node.api.requests.TransactionRequest; import io.hotmoka.node.api.responses.TransactionResponse; import io.hotmoka.node.tendermint.api.TendermintNodeConfig; +import io.hotmoka.stores.StoreException; import io.hotmoka.stores.StoreTransaction; /** @@ -90,7 +91,7 @@ public interface TendermintNodeInternal { /** * Commits the current transaction in the database of the state. */ - void commitTransactionAndCheckout(); + void commitTransactionAndCheckout() throws StoreException; /** * Rewards the validators with the cost of the gas consumed by the diff --git a/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintStore.java b/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintStore.java index 024d4bd0c..7a6debb40 100644 --- a/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintStore.java +++ b/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintStore.java @@ -28,6 +28,7 @@ import io.hotmoka.node.api.transactions.TransactionReference; import io.hotmoka.stores.AbstractTrieBasedStore; import io.hotmoka.stores.StoreException; +import io.hotmoka.xodus.env.Transaction; /** * A partial trie-based store. Errors and requests are recovered by asking @@ -99,14 +100,14 @@ public Optional> getRequest(TransactionReference reference */ byte[] getHash() { try { - synchronized (lock) { - return isEmpty() ? - new byte[0] : // Tendermint requires an empty array at the beginning, for consensus - // we do not use the info part of the hash, so that the hash - // remains stable when the responses and the histories are stable, - // although the info part has changed for the update of the number of commits - hasherOfHashes.hash(mergeRootsOfTriesWithoutInfo()); // we hash the result into 32 bytes - } + synchronized (lock) { + return isEmpty() ? + new byte[0] : // Tendermint requires an empty array at the beginning, for consensus + // we do not use the info part of the hash, so that the hash + // remains stable when the responses and the histories are stable, + // although the info part has changed for the update of the number of commits + hasherOfHashes.hash(mergeRootsOfTriesWithoutInfo()); // we hash the result into 32 bytes + } } catch (StoreException e) { throw new RuntimeException(e); // TODO @@ -124,7 +125,7 @@ byte[] getHash() { * @throws TrieException */ private byte[] mergeRootsOfTriesWithoutInfo() throws StoreException { - byte[] bytes = mergeRootsOfTries(); + byte[] bytes = getStateId(); for (int pos = 32; pos < 64; pos++) bytes[pos] = 0; @@ -147,19 +148,7 @@ protected TendermintStore mkClone(Optional rootOfResponses, Optional request) throws StoreException { - // nothing to do, since Tendermint keeps requests inside its blockchain - return this; - } - - @Override - protected TendermintStore setError(TransactionReference reference, String error) throws StoreException { - // nothing to do, since Tendermint keeps error messages inside the blockchain, in the field "data" of its transactions - return this; - } - - @Override - protected TendermintStoreTransaction mkTransaction() { - return new TendermintStoreTransaction(this, lock); + protected TendermintStoreTransaction mkTransaction(Transaction txn) throws StoreException { + return new TendermintStoreTransaction(this, lock, txn); } } \ No newline at end of file diff --git a/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintStoreTransaction.java b/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintStoreTransaction.java index fd4a66467..369c42cc9 100644 --- a/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintStoreTransaction.java +++ b/io-hotmoka-node-tendermint/src/main/java/io/hotmoka/node/tendermint/internal/TendermintStoreTransaction.java @@ -1,10 +1,24 @@ package io.hotmoka.node.tendermint.internal; +import io.hotmoka.node.api.requests.TransactionRequest; +import io.hotmoka.node.api.transactions.TransactionReference; import io.hotmoka.stores.AbstractTrieBasedStoreTransaction; +import io.hotmoka.stores.StoreException; +import io.hotmoka.xodus.env.Transaction; public class TendermintStoreTransaction extends AbstractTrieBasedStoreTransaction { - protected TendermintStoreTransaction(TendermintStore store, Object lock) { - super(store, lock); + protected TendermintStoreTransaction(TendermintStore store, Object lock, Transaction txn) throws StoreException { + super(store, lock, txn); + } + + @Override + protected void setRequest(TransactionReference reference, TransactionRequest request) throws StoreException { + // nothing to do, since Tendermint keeps requests inside its blockchain + } + + @Override + protected void setError(TransactionReference reference, String error) throws StoreException { + // nothing to do, since Tendermint keeps error messages inside the blockchain, in the field "data" of its transactions } } \ No newline at end of file diff --git a/io-hotmoka-patricia-api/src/main/java/io/hotmoka/patricia/api/PatriciaTrie.java b/io-hotmoka-patricia-api/src/main/java/io/hotmoka/patricia/api/PatriciaTrie.java index 2878ff817..a94f7fb97 100644 --- a/io-hotmoka-patricia-api/src/main/java/io/hotmoka/patricia/api/PatriciaTrie.java +++ b/io-hotmoka-patricia-api/src/main/java/io/hotmoka/patricia/api/PatriciaTrie.java @@ -62,21 +62,4 @@ public interface PatriciaTrie> * @throws TrieException if this Patricia trie is not able to complete the operation correctly */ T checkoutAt(byte[] root) throws TrieException; - - /** - * Yields an independent clone of this trie, but for the supporting store, that is set to the provided value. - * - * @param store the store to use in the cloned trie - * @return the resulting, cloned trie - * @throws TrieException if this Patricia trie is not able to complete the operation correctly - */ - T with(KeyValueStore store) throws TrieException; - - /** - * Garbage-collects all keys that have been updated during the commit with the given number. - * - * @param commitNumber the number of the commit to garbage collect - * @throws TrieException if this Patricia trie is not able to complete the operation correctly - */ - void garbageCollect(long commitNumber) throws TrieException; } \ No newline at end of file diff --git a/io-hotmoka-patricia/src/main/java/io/hotmoka/patricia/internal/AbstractPatriciaTrieImpl.java b/io-hotmoka-patricia/src/main/java/io/hotmoka/patricia/internal/AbstractPatriciaTrieImpl.java index d43b578ec..7cae9d78b 100644 --- a/io-hotmoka-patricia/src/main/java/io/hotmoka/patricia/internal/AbstractPatriciaTrieImpl.java +++ b/io-hotmoka-patricia/src/main/java/io/hotmoka/patricia/internal/AbstractPatriciaTrieImpl.java @@ -20,7 +20,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.ObjectInputStream; -import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Optional; @@ -181,29 +180,6 @@ public final byte[] getRoot() { return root.clone(); } - @Override - public final void garbageCollect(long commitNumber) throws TrieException { - /*long numberOfGarbageKeys = getNumberOfGarbageKeys(commitNumber); - - // there is nothing to remove when numberOfGarbageKeys == 0, since even the - // garbage collection support data is empty - if (numberOfGarbageKeys > 0) { - try { - for (long num = 0; num < numberOfGarbageKeys; num++) - store.remove(getGarbageKey(commitNumber, num)); - } - catch (UnknownKeyException e) { - throw new TrieException("This trie refers to a garbage key that does not exist in the trie itself", e); - } - catch (KeyValueStoreException e) { - throw new TrieException(e); - } - - removeGarbageCollectionData(commitNumber, numberOfGarbageKeys); - } - */ - } - /** * Yields the key/value store that supports this trie. * @@ -753,109 +729,4 @@ protected void markAsGarbageCollectable(byte[] key) throws TrieException { // we disable garbage collection for the empty nodes, since they are not kept in store } } - - /** - * Yields the number of keys that could be garbage collected for the given number of commit. - * - * @param commitNumber the number of commit - * @return the number of keys - * @throws TrieException if this trie is not able to complete the operation correctly - */ - private long getNumberOfGarbageKeys(long commitNumber) throws TrieException { - try { - return bytesToLong(store.get(twoLongsToBytes(commitNumber, 0L))); - } - catch (UnknownKeyException e) { - return 0L; - } - catch (KeyValueStoreException e) { - throw new TrieException(e); - } - } - - /** - * Sets the number of keys that could be garbage collected for the - * given number of commit. - * - * @param commitNumber the number of commit - * @param newNumberOfGarbageKeys the new number of garbage keys to set - * @throws TrieException if this trie is not able to complete the operation correctly - */ - private void setNumberOfGarbageKeys(long commitNumber, long newNumberOfGarbageKeys) throws TrieException { - try { - store.put(twoLongsToBytes(numberOfCommits, 0L), longToBytes(newNumberOfGarbageKeys)); - } - catch (KeyValueStoreException e) { - throw new TrieException(e); - } - } - - /** - * Yields the given key that could be garbage-collected - * because it has been updated during the given number of commit. - * - * @param numberOfCommit the number of commit - * @param keyNumber the progressive number of the key - * @return the key - */ - private byte[] getGarbageKey(long commitNumber, long keyNumber) throws TrieException { - try { - return store.get(twoLongsToBytes(commitNumber, keyNumber + 1)); - } - catch (UnknownKeyException | KeyValueStoreException e) { - throw new TrieException(e); - } - } - - /** - * Sets a key that can be garbage-collected, because it has been updated during - * the given number of commit. - * - * @param commitNumber the number of commit - * @param keyNumber the progressive number of the key updated during the commit - * @param key the updated key - * @throws TrieException if this trie is not able to complete the operation correctly - */ - private void setGarbageKey(long commitNumber, long keyNumber, byte[] key) throws TrieException { - try { - store.put(twoLongsToBytes(commitNumber, keyNumber + 1), key); - } - catch (KeyValueStoreException e) { - throw new TrieException(e); - } - } - - private void removeGarbageCollectionData(long commitNumber, long numberOfGarbageKeys) throws TrieException { - try { - // the 0th is the counter of the keys, the subsequent are the keys; hence the <= - for (long num = 0; num <= numberOfGarbageKeys; num++) - store.remove(twoLongsToBytes(commitNumber, num)); - } - catch (UnknownKeyException e) { - throw new TrieException("This trie refers to a key to garbage collect that cannot be found in the trie itself", e); - } - catch (KeyValueStoreException e) { - throw new TrieException(e); - } - } - - private static byte[] longToBytes(long l) { - var buffer = ByteBuffer.wrap(new byte[Long.BYTES]); - buffer.putLong(l); - return buffer.array(); - } - - private static byte[] twoLongsToBytes(long l1, long l2) { - var buffer = ByteBuffer.wrap(new byte[Long.BYTES * 2]); - buffer.putLong(l1); - buffer.putLong(l2); - return buffer.array(); - } - - private static long bytesToLong(byte[] bytes) { - var buffer = ByteBuffer.allocate(Long.BYTES); - buffer.put(bytes); - buffer.flip(); - return buffer.getLong(); - } } \ No newline at end of file diff --git a/io-hotmoka-patricia/src/main/java/io/hotmoka/patricia/internal/PatriciaTrieImpl.java b/io-hotmoka-patricia/src/main/java/io/hotmoka/patricia/internal/PatriciaTrieImpl.java index 47db08136..13cf61e13 100644 --- a/io-hotmoka-patricia/src/main/java/io/hotmoka/patricia/internal/PatriciaTrieImpl.java +++ b/io-hotmoka-patricia/src/main/java/io/hotmoka/patricia/internal/PatriciaTrieImpl.java @@ -23,7 +23,6 @@ import io.hotmoka.patricia.FromBytes; import io.hotmoka.patricia.ToBytes; import io.hotmoka.patricia.api.KeyValueStore; -import io.hotmoka.patricia.api.TrieException; /** * Implementation of a Merkle-Patricia trie. @@ -82,9 +81,4 @@ private PatriciaTrieImpl(PatriciaTrieImpl cloned, KeyValueStore stor public PatriciaTrieImpl checkoutAt(byte[] root) { return new PatriciaTrieImpl<>(this, root); } - - @Override - public PatriciaTrieImpl with(KeyValueStore store) throws TrieException { - return new PatriciaTrieImpl<>(this, store); - } } \ No newline at end of file diff --git a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/AbstractStore.java b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/AbstractStore.java index a259b1657..bf6b416e6 100644 --- a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/AbstractStore.java +++ b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/AbstractStore.java @@ -16,24 +16,7 @@ package io.hotmoka.stores; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.Stream; - import io.hotmoka.annotations.ThreadSafe; -import io.hotmoka.node.api.requests.InitializationTransactionRequest; -import io.hotmoka.node.api.requests.TransactionRequest; -import io.hotmoka.node.api.responses.GameteCreationTransactionResponse; -import io.hotmoka.node.api.responses.InitializationTransactionResponse; -import io.hotmoka.node.api.responses.TransactionResponse; -import io.hotmoka.node.api.responses.TransactionResponseWithUpdates; -import io.hotmoka.node.api.transactions.TransactionReference; -import io.hotmoka.node.api.updates.Update; -import io.hotmoka.node.api.values.StorageReference; /** * Shared implementation of the store of a node. It keeps information about the state of the objects created @@ -47,8 +30,6 @@ public abstract class AbstractStore> implements Store */ protected final Object lock; - private final static Logger LOGGER = Logger.getLogger(AbstractStore.class.getName()); - /** * Builds the store for a node. */ @@ -71,178 +52,4 @@ protected AbstractStore(AbstractStore toClone) { @Override public void close() {} - - public final T push(TransactionReference reference, TransactionRequest request, TransactionResponse response) throws StoreException { - synchronized (lock) { - T result = setRequest(reference, request); - result = result.setResponse(reference, response); - - if (response instanceof TransactionResponseWithUpdates trwu) { - result = result.expandHistory(reference, trwu); - - if (response instanceof GameteCreationTransactionResponse gctr) - LOGGER.info(gctr.getGamete() + ": created as gamete"); - } - else if (response instanceof InitializationTransactionResponse) { - if (request instanceof InitializationTransactionRequest itr) { - StorageReference manifest = itr.getManifest(); - result = result.setManifest(manifest); - LOGGER.info(manifest + ": set as manifest"); - LOGGER.info("the node has been initialized"); - } - else - throw new StoreException("Trying to initialize the node with a request of class " + request.getClass().getSimpleName()); - } - - return result; - } - } - - public final T push(TransactionReference reference, TransactionRequest request, String errorMessage) throws StoreException { - synchronized (lock) { - T result = setRequest(reference, request); - return result.setError(reference, errorMessage); - } - } - - @Override - public final T replace(TransactionReference reference, TransactionRequest request, TransactionResponse response) throws StoreException { - synchronized (lock) { - return setResponse(reference, response); - } - } - - /** - * Process the updates contained in the given response, expanding the history of the affected objects. - * - * @param reference the transaction that has generated the given response - * @param response the response - * @throws StoreException if this store is not able to complete the operation correctly - */ - protected final T expandHistory(TransactionReference reference, TransactionResponseWithUpdates response) throws StoreException { - // we collect the storage references that have been updated in the response; for each of them, - // we fetch the list of the transaction references that affected them in the past, we add the new transaction reference - // in front of such lists and store back the updated lists, replacing the old ones - var modifiedObjects = response.getUpdates() - .map(Update::getObject) - .distinct().toArray(StorageReference[]::new); - - T result = getThis(); - for (StorageReference object: modifiedObjects) - result = result.setHistory(object, simplifiedHistory(object, reference, response.getUpdates())); - - return result; - } - - /** - * Writes in store the given request for the given transaction reference. - * - * @param reference the reference of the transaction - * @param request the request - * @throws StoreException if this store is not able to complete the operation correctly - */ - protected abstract T setRequest(TransactionReference reference, TransactionRequest request) throws StoreException; - - /** - * Writes in store the given response for the given transaction reference. - * - * @param reference the reference of the transaction - * @param response the response - * @throws StoreException if this store is not able to complete the operation correctly - */ - protected abstract T setResponse(TransactionReference reference, TransactionResponse response) throws StoreException; - - /** - * Writes in store the given error for the given transaction reference. - * - * @param reference the reference of the transaction - * @param error the error - * @throws StoreException if this store is not able to complete the operation correctly - */ - protected abstract T setError(TransactionReference reference, String error) throws StoreException; - - /** - * Sets the history of the given object, that is, - * the references to the transactions that provide information about - * its current state, in reverse chronological order (from newest to oldest). - * - * @param object the object whose history is set - * @param history the stream that will become the history of the object, - * replacing its previous history; this is in chronological order, - * from newest transactions to oldest; hence the last transaction is - * that when the object has been created - */ - protected abstract T setHistory(StorageReference object, Stream history); - - /** - * Mark the node as initialized. This happens for initialization requests. - * - * @param manifest the manifest to put in the node - * @throws StoreException if this store is not able to complete the operation correctly - */ - protected abstract T setManifest(StorageReference manifest) throws StoreException; - - /** - * Adds the given transaction reference to the history of the given object and yields the simplified - * history. Simplification means that some elements of the previous history might not be useful anymore, - * since they get shadowed by the updates in the added transaction reference. This occurs when the values - * 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 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 {@code old} - */ - private Stream simplifiedHistory(StorageReference object, TransactionReference added, Stream addedUpdates) throws StoreException { - Stream old = getHistoryUncommitted(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 covered = addedUpdates.filter(update -> update.getObject().equals(object)).collect(Collectors.toSet()); - var simplified = new ArrayList(); - simplified.add(added); - - var oldAsArray = old.toArray(TransactionReference[]::new); - int length = oldAsArray.length; - for (int pos = 0; pos < length - 1; pos++) - addIfUncovered(oldAsArray[pos], object, covered, simplified); - - // the last is always useful, since it contains at least the class tag of the object - if (length >= 1) - simplified.add(oldAsArray[length - 1]); - - return simplified.stream(); - } - - /** - * 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 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 covered, List history) throws StoreException { - Optional 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 - Set diff = trwu.getUpdates() - .filter(update -> update.getObject().equals(object) && covered.stream().noneMatch(update::sameProperty)) - .collect(Collectors.toSet()); - - if (!diff.isEmpty()) { - // the transaction reference actually adds at least one useful update - history.add(reference); - covered.addAll(diff); - } - } - else - throw new StoreException("The history contains a reference to a transaction without updates"); - } } \ No newline at end of file diff --git a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/AbstractStoreTransaction.java b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/AbstractStoreTransaction.java index 51bf8a8cb..49674ee79 100644 --- a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/AbstractStoreTransaction.java +++ b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/AbstractStoreTransaction.java @@ -43,22 +43,19 @@ */ @ThreadSafe public abstract class AbstractStoreTransaction> implements StoreTransaction { - protected T store; private final Object lock; private final static Logger LOGGER = Logger.getLogger(AbstractStoreTransaction.class.getName()); - protected AbstractStoreTransaction(T store, Object lock) { - this.store = store; + protected AbstractStoreTransaction(Object lock) { this.lock = lock; } @Override public final void push(TransactionReference reference, TransactionRequest request, TransactionResponse response) throws StoreException { synchronized (lock) { - setRequest(reference, request); - setResponse(reference, response); - if (response instanceof TransactionResponseWithUpdates trwu) { + setRequest(reference, request); + setResponse(reference, response); expandHistory(reference, trwu); if (response instanceof GameteCreationTransactionResponse gctr) @@ -66,6 +63,8 @@ public final void push(TransactionReference reference, TransactionRequest req } else if (response instanceof InitializationTransactionResponse) { if (request instanceof InitializationTransactionRequest itr) { + setRequest(reference, request); + setResponse(reference, response); StorageReference manifest = itr.getManifest(); setManifest(manifest); LOGGER.info(manifest + ": set as manifest"); @@ -74,6 +73,10 @@ else if (response instanceof InitializationTransactionResponse) { else throw new StoreException("Trying to initialize the node with a request of class " + request.getClass().getSimpleName()); } + else { + setRequest(reference, request); + setResponse(reference, response); + } } } @@ -92,25 +95,6 @@ public final void replace(TransactionReference reference, TransactionRequest } } - /** - * Process the updates contained in the given response, expanding the history of the affected objects. - * - * @param reference the transaction that has generated the given response - * @param response the response - * @throws StoreException if this store is not able to complete the operation correctly - */ - protected final void expandHistory(TransactionReference reference, TransactionResponseWithUpdates response) throws StoreException { - // we collect the storage references that have been updated in the response; for each of them, - // we fetch the list of the transaction references that affected them in the past, we add the new transaction reference - // in front of such lists and store back the updated lists, replacing the old ones - var modifiedObjects = response.getUpdates() - .map(Update::getObject) - .distinct().toArray(StorageReference[]::new); - - for (StorageReference object: modifiedObjects) - setHistory(object, simplifiedHistory(object, reference, response.getUpdates())); - } - /** * Writes in store the given request for the given transaction reference. * @@ -149,7 +133,7 @@ protected final void expandHistory(TransactionReference reference, TransactionRe * from newest transactions to oldest; hence the last transaction is * that when the object has been created */ - protected abstract void setHistory(StorageReference object, Stream history); + protected abstract void setHistory(StorageReference object, Stream history) throws StoreException; /** * Mark the node as initialized. This happens for initialization requests. @@ -159,6 +143,25 @@ protected final void expandHistory(TransactionReference reference, TransactionRe */ protected abstract void setManifest(StorageReference manifest) throws StoreException; + /** + * Process the updates contained in the given response, expanding the history of the affected objects. + * + * @param reference the transaction that has generated the given response + * @param response the response + * @throws StoreException if this store is not able to complete the operation correctly + */ + private void expandHistory(TransactionReference reference, TransactionResponseWithUpdates response) throws StoreException { + // we collect the storage references that have been updated in the response; for each of them, + // we fetch the list of the transaction references that affected them in the past, we add the new transaction reference + // in front of such lists and store back the updated lists, replacing the old ones + var modifiedObjects = response.getUpdates() + .map(Update::getObject) + .distinct().toArray(StorageReference[]::new); + + for (StorageReference object: modifiedObjects) + setHistory(object, simplifiedHistory(object, reference, response.getUpdates())); + } + /** * Adds the given transaction reference to the history of the given object and yields the simplified * history. Simplification means that some elements of the previous history might not be useful anymore, @@ -172,7 +175,7 @@ protected final void expandHistory(TransactionReference reference, TransactionRe * @return the simplified history, with {@code added} in front followed by a subset of {@code old} */ private Stream simplifiedHistory(StorageReference object, TransactionReference added, Stream addedUpdates) throws StoreException { - Stream old = getHistory(object); + Stream old = getHistoryUncommitted(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 @@ -202,7 +205,7 @@ private Stream simplifiedHistory(StorageReference object, * @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 covered, List history) throws StoreException { - Optional maybeResponse = getResponse(reference); + Optional maybeResponse = getResponseUncommitted(reference); if (maybeResponse.isEmpty()) throw new StoreException("The history contains a reference to a transaction not in store"); diff --git a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/AbstractTrieBasedStore.java b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/AbstractTrieBasedStore.java index fb9cae0d5..9e8a20050 100644 --- a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/AbstractTrieBasedStore.java +++ b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/AbstractTrieBasedStore.java @@ -99,52 +99,27 @@ public abstract class AbstractTrieBasedStore /** * The root of the trie of the responses. It is empty if the trie is empty. */ - private Optional rootOfResponses; + private final Optional rootOfResponses; /** * The root of the trie of the miscellaneous info. It is empty if the trie is empty. */ - private Optional rootOfInfo; + private final Optional rootOfInfo; /** * The root of the trie of the errors. It is empty if the trie is empty. */ - private Optional rootOfErrors = Optional.empty(); + private final Optional rootOfErrors; /** * The root of the trie of the requests. It is empty if the trie is empty. */ - private Optional rootOfRequests = Optional.empty(); + private final Optional rootOfRequests; /** * The root of the trie of histories. It is empty if the trie is empty. */ - private Optional rootOfHistories = Optional.empty(); - - /** - * The trie of the responses. - */ - private TrieOfResponses trieOfResponses; - - /** - * The trie for the miscellaneous information. - */ - private TrieOfInfo trieOfInfo; - - /** - * The trie of the errors. - */ - protected TrieOfErrors trieOfErrors; - - /** - * The trie of the requests. - */ - protected TrieOfRequests trieOfRequests; - - /** - * The trie of histories. - */ - private TrieOfHistories trieOfHistories; + private final Optional rootOfHistories; /** * The key used inside {@link #storeOfInfo} to keep the root. @@ -159,10 +134,9 @@ public abstract class AbstractTrieBasedStore private final static Logger LOGGER = Logger.getLogger(AbstractTrieBasedStore.class.getName()); /** - * Creates a store. Its roots are not yet initialized. Hence, after this constructor, - * a call to {@link #setRootsTo(Optional)} should occur, to set the roots of the store. + * Creates a store. Its roots are initialized as in the Xodus store, if present. * - * @param dir the path where the database of the store gets created + * @param dir the path where the database of the store is kept */ protected AbstractTrieBasedStore(Path dir) { this.env = new Environment(dir + "/store"); @@ -241,11 +215,6 @@ protected AbstractTrieBasedStore(AbstractTrieBasedStore toClone) { this.rootOfErrors = toClone.rootOfErrors; this.rootOfHistories = toClone.rootOfHistories; this.rootOfRequests = toClone.rootOfRequests; - this.trieOfResponses = toClone.trieOfResponses; - this.trieOfInfo = toClone.trieOfInfo; - this.trieOfErrors = toClone.trieOfErrors; - this.trieOfHistories = toClone.trieOfHistories; - this.trieOfRequests = toClone.trieOfRequests; this.txn = toClone.txn; } } @@ -266,11 +235,6 @@ protected AbstractTrieBasedStore(AbstractTrieBasedStore toClone, Optional toClone, Optional getResponse(TransactionReference reference) { synchronized (lock) { return env.computeInReadonlyTransaction // TODO: recheck - (UncheckFunction.uncheck(txn -> new TrieOfResponses(new KeyValueStoreOnXodus(storeOfResponses, txn), rootOfResponses).get(reference))); + (UncheckFunction.uncheck(txn -> mkTrieOfResponses(txn).get(reference))); } } - @Override - public Optional getResponseUncommitted(TransactionReference reference) { - synchronized (lock) { - try { - return duringTransaction() ? trieOfResponses.get(reference) : getResponse(reference); - } - catch (TrieException e) { - throw new RuntimeException(e); // TODO - } - } - } - @Override public Optional getManifest() throws StoreException { try { synchronized (lock) { return CheckSupplier.check(TrieException.class, () -> - env.computeInReadonlyTransaction(UncheckFunction.uncheck(txn -> new TrieOfInfo(new KeyValueStoreOnXodus(storeOfInfo, txn), rootOfInfo).getManifest()))); + env.computeInReadonlyTransaction(UncheckFunction.uncheck(txn -> mkTrieOfInfo(txn).getManifest()))); } } catch (ExodusException | TrieException e) { @@ -328,24 +280,12 @@ public Optional getManifest() throws StoreException { } } - @Override - public Optional getManifestUncommitted() throws StoreException { - try { - synchronized (lock) { - return duringTransaction() ? trieOfInfo.getManifest() : getManifest(); - } - } - catch (TrieException e) { - throw new StoreException(e); - } - } - @Override public Optional getError(TransactionReference reference) throws StoreException { synchronized (lock) { try { return CheckSupplier.check(TrieException.class, () -> env.computeInReadonlyTransaction - (UncheckFunction.uncheck(txn -> new TrieOfErrors(new KeyValueStoreOnXodus(storeOfErrors, txn), rootOfErrors).get(reference)))); + (UncheckFunction.uncheck(txn -> mkTrieOfErrors(txn).get(reference)))); } catch (TrieException e) { throw new StoreException(e); @@ -357,7 +297,7 @@ public Optional getError(TransactionReference reference) throws StoreExc public Optional> getRequest(TransactionReference reference) { synchronized (lock) { return env.computeInReadonlyTransaction // TODO: recheck - (UncheckFunction.uncheck(txn -> new TrieOfRequests(new KeyValueStoreOnXodus(storeOfRequests, txn), rootOfRequests).get(reference))); + (UncheckFunction.uncheck(txn -> mkTrieOfRequests(txn).get(reference))); } } @@ -366,7 +306,7 @@ public Stream getHistory(StorageReference object) throws S try { synchronized (lock) { return CheckSupplier.check(TrieException.class, () -> env.computeInReadonlyTransaction - (UncheckFunction.uncheck(txn -> new TrieOfHistories(new KeyValueStoreOnXodus(storeOfHistories, txn), rootOfHistories).get(object))).orElse(Stream.empty())); + (UncheckFunction.uncheck(txn -> mkTrieOfHistories(txn).get(object))).orElse(Stream.empty())); } } catch (TrieException e) { @@ -375,73 +315,8 @@ public Stream getHistory(StorageReference object) throws S } @Override - public Stream getHistoryUncommitted(StorageReference object) throws StoreException { - synchronized (lock) { - try { - return duringTransaction() ? trieOfHistories.get(object).orElse(Stream.empty()) : getHistory(object); - } - catch (TrieException e) { - throw new StoreException(e); - } - } - } - - @Override - public StoreTransaction beginTransaction() { - synchronized (lock) { - txn = env.beginTransaction(); - - try { - trieOfResponses = new TrieOfResponses(new KeyValueStoreOnXodus(storeOfResponses, txn), rootOfResponses); - trieOfInfo = new TrieOfInfo(new KeyValueStoreOnXodus(storeOfInfo, txn), rootOfInfo); - trieOfErrors = new TrieOfErrors(new KeyValueStoreOnXodus(storeOfErrors, txn), rootOfErrors); - trieOfRequests = new TrieOfRequests(new KeyValueStoreOnXodus(storeOfRequests, txn), rootOfRequests); - trieOfHistories = new TrieOfHistories(new KeyValueStoreOnXodus(storeOfHistories, txn), rootOfHistories); - } - catch (TrieException e) { - throw new RuntimeException(e); // TODO - } - - return mkTransaction(); - } - } - - protected abstract StoreTransaction mkTransaction(); - - /** - * Commits to the database all data written from the last call to {@link #beginTransactionInternal()}. - * This does not change the view of the store, since its roots are not updated, - * unless the hash returned by this method gets later checked out to update the roots. - * - * @return the hash of the store resulting at the end of all updates performed during the transaction; - * if this gets checked out, the view of the store becomes that at the end of the transaction - */ - public final T endTransaction() { - try { - synchronized (lock) { - trieOfInfo = trieOfInfo.increaseNumberOfCommits(); - //long newCommitNumber = trieOfInfo.getNumberOfCommits(); - - // a negative number means that garbage-collection is disabled - /*if (checkableDepth >= 0L) { - long commitToGarbageCollect = newCommitNumber - 1 - checkableDepth; - if (commitToGarbageCollect >= 0L) - garbageCollect(commitToGarbageCollect); - }*/ - - if (!txn.commit()) - LOGGER.info("transaction's commit failed"); - - T result = mkClone(); - result.setRootsTo(result.mergeRootsOfTries()); - result.moveRootBranchToThis(); - - return result; - } - } - catch (StoreException | TrieException e) { - throw new RuntimeException(e); // TODO - } + public StoreTransaction beginTransaction() throws StoreException { + return mkTransaction(env.beginTransaction()); } /** @@ -451,143 +326,84 @@ public final T endTransaction() { */ public long getNumberOfCommits() { return env.computeInReadonlyTransaction // TODO: recheck - (UncheckFunction.uncheck(txn -> new TrieOfInfo(new KeyValueStoreOnXodus(storeOfInfo, txn), rootOfInfo).getNumberOfCommits())); + (UncheckFunction.uncheck(txn -> mkTrieOfInfo(txn).getNumberOfCommits())); } public byte[] getStateId() throws StoreException { return mergeRootsOfTries(); } - @Override - protected T setResponse(TransactionReference reference, TransactionResponse response) throws StoreException { - try { - TrieOfResponses newTrieOfResponses = trieOfResponses.put(reference, response); - this.trieOfResponses = newTrieOfResponses; - return mkClone(); // TODO Optional.of(newTrieOfResponses.getRoot()), rootOfInfo); - } - catch (TrieException e) { - throw new StoreException(e); - } + /** + * Resets the store to the given root. This is just the concatenation of the roots + * of the tries in this store. For instance, as returned by a previous {@link #commitTransaction()}. + * + * @param root the root to reset to + */ + public T checkoutAt(byte[] root) { + var bytesOfRootOfResponses = new byte[32]; + System.arraycopy(root, 0, bytesOfRootOfResponses, 0, 32); + var bytesOfRootOfInfo = new byte[32]; + System.arraycopy(root, 32, bytesOfRootOfInfo, 0, 32); + var bytesOfRootOfErrors = new byte[32]; + System.arraycopy(root, 64, bytesOfRootOfErrors, 0, 32); + var bytesOfRootOfRequests = new byte[32]; + System.arraycopy(root, 96, bytesOfRootOfRequests, 0, 32); + var bytesOfRootOfHistories = new byte[32]; + System.arraycopy(root, 128, bytesOfRootOfHistories, 0, 32); + + return mkClone(Optional.of(bytesOfRootOfResponses), Optional.of(bytesOfRootOfInfo), Optional.of(bytesOfRootOfErrors), Optional.of(bytesOfRootOfHistories), Optional.of(bytesOfRootOfRequests)); } - @Override - protected T setManifest(StorageReference manifest) throws StoreException { + public void moveRootBranchToThis() throws StoreException { + var rootAsBI = ByteIterable.fromBytes(mergeRootsOfTries()); + env.executeInTransaction(txn -> storeOfInfo.put(txn, ROOT, rootAsBI)); + } + + protected abstract StoreTransaction mkTransaction(Transaction txn) throws StoreException; + + protected TrieOfResponses mkTrieOfResponses(Transaction txn) throws StoreException { try { - TrieOfInfo newTrieOfInfo = trieOfInfo.setManifest(manifest); - this.trieOfInfo = newTrieOfInfo; - return mkClone(); //rootOfResponses, Optional.of(newTrieOfInfo.getRoot())); + return new TrieOfResponses(new KeyValueStoreOnXodus(storeOfResponses, txn), rootOfResponses); } catch (TrieException e) { throw new StoreException(e); } } - @Override - protected T setRequest(TransactionReference reference, TransactionRequest request) throws StoreException { + protected TrieOfInfo mkTrieOfInfo(Transaction txn) throws StoreException { try { - T result = getThis(); - result.trieOfRequests = result.trieOfRequests.put(reference, request); - return result.mkClone(); + return new TrieOfInfo(new KeyValueStoreOnXodus(storeOfInfo, txn), rootOfInfo); } catch (TrieException e) { throw new StoreException(e); } } - @Override - protected T setError(TransactionReference reference, String error) throws StoreException { + protected TrieOfErrors mkTrieOfErrors(Transaction txn) throws StoreException { try { - T result = getThis(); - result.trieOfErrors = result.trieOfErrors.put(reference, error); - return result.mkClone(); + return new TrieOfErrors(new KeyValueStoreOnXodus(storeOfErrors, txn), rootOfErrors); } catch (TrieException e) { throw new StoreException(e); } } - @Override - protected T setHistory(StorageReference object, Stream history) { + protected TrieOfRequests mkTrieOfRequests(Transaction txn) throws StoreException { try { - trieOfHistories = trieOfHistories.put(object, history); - return mkClone(); + return new TrieOfRequests(new KeyValueStoreOnXodus(storeOfRequests, txn), rootOfRequests); } catch (TrieException e) { - throw new RuntimeException(e); // TODO + throw new StoreException(e); } } - /** - * Garbage-collects all keys updated during the given commit. - * - * @param commitNumber the number of the commit - * @throws StoreException if this store is not able to complete the operation correctly - */ - /*protected void garbageCollect(long commitNumber) throws StoreException { + protected TrieOfHistories mkTrieOfHistories(Transaction txn) throws StoreException { try { - trieOfResponses.garbageCollect(commitNumber); - trieOfInfo.garbageCollect(commitNumber); + return new TrieOfHistories(new KeyValueStoreOnXodus(storeOfHistories, txn), rootOfHistories); } catch (TrieException e) { throw new StoreException(e); } - }*/ - - /** - * Resets the store to the given root. This is just the concatenation of the roots - * of the tries in this store. For instance, as returned by a previous {@link #commitTransaction()}. - * - * @param root the root to reset to - */ - public void checkoutAt(byte[] root) { - synchronized (lock) { - setRootsTo(root); - } - } - - public void moveRootBranchToThis() throws StoreException { - synchronized (lock) { - byte[] root = mergeRootsOfTries(); - var rootAsBI = ByteIterable.fromBytes(root); - env.executeInTransaction(txn -> storeOfInfo.put(txn, ROOT, rootAsBI)); - } - } - - /** - * Determines if the store is between a {@link #beginTransactionInternal()} and a - * {@link #endTransaction()}. - * - * @return true if and only if that condition holds - */ - protected final boolean duringTransaction() { - return txn != null && !txn.isFinished(); - } - - /** - * Sets the roots of this store to the given (merged) root. - * - * @param root the merged root; this is empty if the store is empty and has consequently no root yet - */ - protected void setRootsTo(byte[] root) { - var bytesOfRootOfResponses = new byte[32]; - System.arraycopy(root, 0, bytesOfRootOfResponses, 0, 32); - rootOfResponses = Optional.of(bytesOfRootOfResponses); - - var bytesOfRootOfInfo = new byte[32]; - System.arraycopy(root, 32, bytesOfRootOfInfo, 0, 32); - rootOfInfo = Optional.of(bytesOfRootOfInfo); - - var bytesOfRootOfErrors = new byte[32]; - System.arraycopy(root, 64, bytesOfRootOfErrors, 0, 32); - rootOfErrors = Optional.of(bytesOfRootOfErrors); - - var bytesOfRootOfRequests = new byte[32]; - System.arraycopy(root, 96, bytesOfRootOfRequests, 0, 32); - rootOfRequests = Optional.of(bytesOfRootOfRequests); - - var bytesOfRootOfHistories = new byte[32]; - System.arraycopy(root, 128, bytesOfRootOfHistories, 0, 32); - rootOfHistories = Optional.of(bytesOfRootOfHistories); } /** @@ -597,13 +413,13 @@ protected void setRootsTo(byte[] root) { * * @return the concatenation */ - protected byte[] mergeRootsOfTries() throws StoreException { + private byte[] mergeRootsOfTries() throws StoreException { var result = new byte[160]; - System.arraycopy(trieOfResponses.getRoot(), 0, result, 0, 32); - System.arraycopy(trieOfInfo.getRoot(), 0, result, 32, 32); - System.arraycopy(trieOfErrors.getRoot(), 0, result, 64, 32); - System.arraycopy(trieOfRequests.getRoot(), 0, result, 96, 32); - System.arraycopy(trieOfHistories.getRoot(), 0, result, 128, 32); + System.arraycopy(rootOfResponses.get(), 0, result, 0, 32); + System.arraycopy(rootOfInfo.get(), 0, result, 32, 32); + System.arraycopy(rootOfErrors.get(), 0, result, 64, 32); + System.arraycopy(rootOfRequests.get(), 0, result, 96, 32); + System.arraycopy(rootOfHistories.get(), 0, result, 128, 32); return result; } diff --git a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/AbstractTrieBasedStoreTransaction.java b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/AbstractTrieBasedStoreTransaction.java index 056179c49..ccd1a7f30 100644 --- a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/AbstractTrieBasedStoreTransaction.java +++ b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/AbstractTrieBasedStoreTransaction.java @@ -7,59 +7,161 @@ import io.hotmoka.node.api.responses.TransactionResponse; import io.hotmoka.node.api.transactions.TransactionReference; import io.hotmoka.node.api.values.StorageReference; +import io.hotmoka.patricia.api.TrieException; +import io.hotmoka.stores.internal.TrieOfErrors; +import io.hotmoka.stores.internal.TrieOfHistories; +import io.hotmoka.stores.internal.TrieOfInfo; +import io.hotmoka.stores.internal.TrieOfRequests; +import io.hotmoka.stores.internal.TrieOfResponses; +import io.hotmoka.xodus.env.Transaction; public abstract class AbstractTrieBasedStoreTransaction> extends AbstractStoreTransaction { - protected AbstractTrieBasedStoreTransaction(T store, Object lock) { - super(store, lock); + /** + * The Xodus transaction where the updates get recorded. + */ + private final Transaction txn; + + /** + * The store from which this transaction was started. + */ + private final T store; + + /** + * The trie of the responses. + */ + private volatile TrieOfResponses trieOfResponses; + + /** + * The trie for the miscellaneous information. + */ + private volatile TrieOfInfo trieOfInfo; + + /** + * The trie of the errors. + */ + private volatile TrieOfErrors trieOfErrors; + + /** + * The trie of histories. + */ + private volatile TrieOfHistories trieOfHistories; + + /** + * The trie of the requests. + */ + private volatile TrieOfRequests trieOfRequests; + + protected AbstractTrieBasedStoreTransaction(T store, Object lock, Transaction txn) throws StoreException { + super(lock); + + this.txn = txn; + this.store = store; + this.trieOfResponses = store.mkTrieOfResponses(txn); + this.trieOfInfo = store.mkTrieOfInfo(txn); + this.trieOfErrors = store.mkTrieOfErrors(txn); + this.trieOfHistories = store.mkTrieOfHistories(txn); + this.trieOfRequests = store.mkTrieOfRequests(txn); } @Override - public Optional getResponse(TransactionReference reference) { - System.out.println("response"); - return store.getResponseUncommitted(reference); + public Optional getResponseUncommitted(TransactionReference reference) throws StoreException{ + try { + return trieOfResponses.get(reference); + } + catch (TrieException e) { + throw new StoreException(e); + } } @Override - public Stream getHistory(StorageReference object) throws StoreException { - System.out.println("history"); - new Exception().printStackTrace(); - return store.getHistoryUncommitted(object); + public Stream getHistoryUncommitted(StorageReference object) throws StoreException { + try { + return trieOfHistories.get(object).orElse(Stream.empty()); // TODO + } + catch (TrieException e) { + throw new StoreException(e); + } } @Override - public Optional getManifest() throws StoreException { - System.out.println("manifest"); - return store.getManifestUncommitted(); + public Optional getManifestUncommitted() throws StoreException { + try { + return trieOfInfo.getManifest(); + } + catch (TrieException e) { + throw new StoreException(e); + } } @Override protected void setRequest(TransactionReference reference, TransactionRequest request) throws StoreException { - store = store.setRequest(reference, request); + try { + trieOfRequests = trieOfRequests.put(reference, request); + } + catch (TrieException e) { + throw new StoreException(e); + } } @Override protected void setResponse(TransactionReference reference, TransactionResponse response) throws StoreException { - store = store.setResponse(reference, response); + try { + trieOfResponses = trieOfResponses.put(reference, response); + } + catch (TrieException e) { + throw new StoreException(e); + } } @Override protected void setError(TransactionReference reference, String error) throws StoreException { - store = store.setError(reference, error); + try { + trieOfErrors = trieOfErrors.put(reference, error); + } + catch (TrieException e) { + throw new StoreException(e); + } } @Override - protected void setHistory(StorageReference object, Stream history) { - store = store.setHistory(object, history); + protected void setHistory(StorageReference object, Stream history) throws StoreException { + try { + trieOfHistories = trieOfHistories.put(object, history); + } + catch (TrieException e) { + throw new StoreException(e); + } } @Override protected void setManifest(StorageReference manifest) throws StoreException { - store = store.setManifest(manifest); + try { + trieOfInfo = trieOfInfo.setManifest(manifest); + } + catch (TrieException e) { + throw new StoreException(e); + } } @Override - public T commit() { - return store; + public T commit() throws StoreException { + try { + trieOfInfo = trieOfInfo.increaseNumberOfCommits(); + } + catch (TrieException e) { + throw new StoreException(e); + } + + if (!txn.commit()) + throw new StoreException("Cannot commit the Xodus transaction"); + + return store.mkClone( + Optional.of(trieOfResponses.getRoot()), + Optional.of(trieOfInfo.getRoot()), + Optional.of(trieOfErrors.getRoot()), + Optional.of(trieOfHistories.getRoot()), + Optional.of(trieOfRequests.getRoot()) + ); } } \ No newline at end of file diff --git a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/Store.java b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/Store.java index 2a2b2d022..b45222702 100644 --- a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/Store.java +++ b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/Store.java @@ -42,15 +42,6 @@ public interface Store> extends AutoCloseable { */ Optional getResponse(TransactionReference reference); - /** - * Yields the response of the transaction having the given reference. - * The response is returned also when it is not committed yet. - * - * @param reference the reference of the transaction - * @return the response, if any - */ - Optional getResponseUncommitted(TransactionReference reference); - /** * Yields the error generated by the transaction having the given reference. * @@ -70,17 +61,6 @@ public interface Store> extends AutoCloseable { */ Stream getHistory(StorageReference object) throws StoreException; - /** - * Yields the history of the given object, that is, the references of the transactions - * that can be used to reconstruct the current values of its fields. This considers also - * updates of the transaction not committed yet. - * - * @param object the reference of the object - * @return the history. Yields an empty stream if there is no history for {@code object} - * @throws StoreException if the store is not able to complete the operation correctly - */ - Stream getHistoryUncommitted(StorageReference object) throws StoreException; - /** * Yields the manifest installed when the node is initialized. * @@ -89,15 +69,6 @@ public interface Store> extends AutoCloseable { */ Optional getManifest() throws StoreException; - /** - * Yields the manifest installed when the node is initialized, also when the - * transaction that installed it is not committed yet. - * - * @return the manifest - * @throws StoreException if the store is not able to complete the operation correctly - */ - Optional getManifestUncommitted() throws StoreException; - /** * Yields the request that generated the transaction with the given reference. * If this node has some form of commit, then this method is called only when @@ -112,42 +83,5 @@ public interface Store> extends AutoCloseable { * Starts a transaction. Instance updates during the transaction are saved * in the supporting database if the transaction will later be committed. */ - StoreTransaction beginTransaction(); - - /** - * Pushes the result of executing a successful Hotmoka request. - * This method assumes that the given request was not already present in the store. - * This method yields a store where the push is visible. Checkable stores remain - * unchanged after a call to this method, while non-checkable stores might be - * modified and coincide with the result of the method. - * - * @param reference the reference of the request - * @param request the request of the transaction - * @param response the response of the transaction - * @return the store resulting after the push - * @throws StoreException if the store is not able to complete the operation correctly - */ - //T push(TransactionReference reference, TransactionRequest request, TransactionResponse response) throws StoreException; - - /** - * Pushes into the store the error message resulting from the unsuccessful execution of a Hotmoka request. - * - * @param reference the reference of the request - * @param request the request of the transaction - * @param errorMessage the error message - * @return the store resulting after the push - * @throws StoreException if the store is not able to complete the operation correctly - */ - //T push(TransactionReference reference, TransactionRequest request, String errorMessage) throws StoreException; - - /** - * Pushes into the store the result of executing a successful Hotmoka request. - * This method assumes that the given request was already present in the store. - * - * @param reference the reference of the request - * @param request the request of the transaction - * @param response the response of the transaction - * @throws StoreException if the store is not able to complete the operation correctly - */ - T replace(TransactionReference reference, TransactionRequest request, TransactionResponse response) throws StoreException; + StoreTransaction beginTransaction() throws StoreException; } \ No newline at end of file diff --git a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/StoreTransaction.java b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/StoreTransaction.java index d52868d71..7767ae6cf 100644 --- a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/StoreTransaction.java +++ b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/StoreTransaction.java @@ -36,29 +36,32 @@ public interface StoreTransaction> { /** * 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 */ - Optional getResponse(TransactionReference reference); + Optional getResponseUncommitted(TransactionReference reference) throws StoreException; /** * Yields the history of the given object, that is, the references to the transactions * that can be used to reconstruct the current values of its fields. + * This considers also updates inside this transaction, that have not yet been committed. * * @param object the reference of the object * @return the history. Yields an empty stream if there is no history for {@code object} * @throws StoreException if the store is not able to perform the operation */ - Stream getHistory(StorageReference object) throws StoreException; + Stream getHistoryUncommitted(StorageReference object) throws StoreException; /** * Yields the manifest installed when the node is initialized. + * This considers also updates inside this transaction, that have not yet been committed. * * @return the manifest * @throws StoreException if the store is not able to complete the operation correctly */ - Optional getManifest() throws StoreException; + Optional getManifestUncommitted() throws StoreException; /** * Pushes the result of executing a successful Hotmoka request. @@ -97,5 +100,5 @@ public interface StoreTransaction> { */ void replace(TransactionReference reference, TransactionRequest request, TransactionResponse response) throws StoreException; - T commit(); + T commit() throws StoreException; } \ No newline at end of file diff --git a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfErrors.java b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfErrors.java index a62e31e91..b7197c2d9 100644 --- a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfErrors.java +++ b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfErrors.java @@ -62,11 +62,6 @@ public TrieOfErrors checkoutAt(byte[] root) { return new TrieOfErrors(this, root); } - @Override - public TrieOfErrors with(KeyValueStore store) { - return new TrieOfErrors(this, store); - } - private static HashingAlgorithm sha256() throws TrieException { try { return HashingAlgorithms.sha256(); diff --git a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfHistories.java b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfHistories.java index dfe702565..2506d6750 100644 --- a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfHistories.java +++ b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfHistories.java @@ -97,9 +97,4 @@ public TrieOfHistories put(StorageReference key, Stream hi public TrieOfHistories checkoutAt(byte[] root) { return new TrieOfHistories(this, root); } - - @Override - public TrieOfHistories with(KeyValueStore store) throws TrieException { - return new TrieOfHistories(this, store); - } } \ No newline at end of file diff --git a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfInfo.java b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfInfo.java index 385504690..7923c17c4 100644 --- a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfInfo.java +++ b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfInfo.java @@ -61,11 +61,6 @@ public TrieOfInfo checkoutAt(byte[] root) { return new TrieOfInfo(this, root); } - @Override - public TrieOfInfo with(KeyValueStore store) throws TrieException { - return new TrieOfInfo(this, store); - } - private static HashingAlgorithm sha256() throws TrieException { try { return HashingAlgorithms.sha256(); diff --git a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfRequests.java b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfRequests.java index 23b4c701c..415517d08 100644 --- a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfRequests.java +++ b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfRequests.java @@ -54,11 +54,6 @@ private TrieOfRequests(TrieOfRequests cloned, KeyValueStore store) { super(cloned, store); } - @Override - public TrieOfRequests with(KeyValueStore store) throws TrieException { - return new TrieOfRequests(this, store); - } - @Override public TrieOfRequests checkoutAt(byte[] root) { return new TrieOfRequests(this, root); diff --git a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfResponses.java b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfResponses.java index 1c825f79c..f1d0f2e77 100644 --- a/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfResponses.java +++ b/io-hotmoka-stores/src/main/java/io/hotmoka/stores/internal/TrieOfResponses.java @@ -91,11 +91,6 @@ public TrieOfResponses put(TransactionReference key, TransactionResponse value) return super.put(key, writeTransformation(value)); } - @Override - public TrieOfResponses with(KeyValueStore store) throws TrieException { - return new TrieOfResponses(this, store); - } - @Override public TrieOfResponses checkoutAt(byte[] root) { return new TrieOfResponses(this, root); diff --git a/io-hotmoka-stores/src/main/java/module-info.java b/io-hotmoka-stores/src/main/java/module-info.java index a125acaae..d7b688b93 100644 --- a/io-hotmoka-stores/src/main/java/module-info.java +++ b/io-hotmoka-stores/src/main/java/module-info.java @@ -26,6 +26,6 @@ requires io.hotmoka.crypto; requires io.hotmoka.patricia; requires io.hotmoka.exceptions; - requires io.hotmoka.xodus; + requires transitive io.hotmoka.xodus; requires java.logging; } \ No newline at end of file diff --git a/io-hotmoka-tests/pom.xml b/io-hotmoka-tests/pom.xml index 57ec8f70e..4084d76ee 100644 --- a/io-hotmoka-tests/pom.xml +++ b/io-hotmoka-tests/pom.xml @@ -85,12 +85,6 @@ ${hotmoka.version} test - - io.hotmoka - io-hotmoka-examples - ${hotmoka.version} - runtime - org.jboss.shrinkwrap.resolver shrinkwrap-resolver-api-maven diff --git a/io-hotmoka-tests/src/test/java/io/hotmoka/tests/Bombing.java b/io-hotmoka-tests/src/test/java/io/hotmoka/tests/Bombing.java index 3f4ce495b..f0020d945 100644 --- a/io-hotmoka-tests/src/test/java/io/hotmoka/tests/Bombing.java +++ b/io-hotmoka-tests/src/test/java/io/hotmoka/tests/Bombing.java @@ -49,7 +49,7 @@ /** * A test for generating many coin transfers and count their speed. */ -class Bombing extends HotmokaTest { +public class Bombing extends HotmokaTest { private final static int NUMBER_OF_TRANSFERS = 1000; private static int NUMBER_OF_ACCOUNTS = 500; diff --git a/io-hotmoka-tests/src/test/java/io/hotmoka/tests/GetResponse.java b/io-hotmoka-tests/src/test/java/io/hotmoka/tests/GetResponse.java index 5bfb799f5..dbdef57ab 100644 --- a/io-hotmoka-tests/src/test/java/io/hotmoka/tests/GetResponse.java +++ b/io-hotmoka-tests/src/test/java/io/hotmoka/tests/GetResponse.java @@ -45,7 +45,7 @@ import io.hotmoka.node.api.values.StorageReference; /** - * A test for {@link io.hotmoka.node.api.Node#getResponse(io.hotmoka.beans.api.transactions.TransactionReference)}. + * A test for {@link io.hotmoka.node.api.Node#getResponseUncommited(io.hotmoka.beans.api.transactions.TransactionReference)}. */ class GetResponse extends HotmokaTest { private static final ConstructorSignature ABSTRACT_FAIL_IMPL_CONSTRUCTOR = ConstructorSignatures.of(StorageTypes.classNamed("io.hotmoka.examples.abstractfail.AbstractFailImpl"), StorageTypes.INT); diff --git a/io-hotmoka-tests/src/test/java/io/hotmoka/tests/HotmokaTest.java b/io-hotmoka-tests/src/test/java/io/hotmoka/tests/HotmokaTest.java index 36e59dbcf..572a4ba81 100644 --- a/io-hotmoka-tests/src/test/java/io/hotmoka/tests/HotmokaTest.java +++ b/io-hotmoka-tests/src/test/java/io/hotmoka/tests/HotmokaTest.java @@ -289,7 +289,7 @@ private static Node mkDiskBlockchain() throws NoSuchAlgorithmException, InvalidK var config = DiskNodeConfigBuilders.defaults() .setMaxGasPerViewTransaction(_10_000_000) - .setMaxPollingAttempts(10) // we fix these two so that we know the timeout in case of problems + .setMaxPollingAttempts(100) // we fix these two so that we know the timeout in case of problems .setPollingDelay(10) .build(); diff --git a/io-hotmoka-tests/src/test/java/io/hotmoka/tests/NodeFromNetwork.java b/io-hotmoka-tests/src/test/java/io/hotmoka/tests/NodeFromNetwork.java index eca29b4bc..27de79df3 100644 --- a/io-hotmoka-tests/src/test/java/io/hotmoka/tests/NodeFromNetwork.java +++ b/io-hotmoka-tests/src/test/java/io/hotmoka/tests/NodeFromNetwork.java @@ -26,7 +26,6 @@ import java.math.BigInteger; import java.net.URI; -import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -56,7 +55,6 @@ import io.hotmoka.node.api.values.StorageReference; import io.hotmoka.node.api.values.StringValue; import io.hotmoka.node.remote.RemoteNodes; -import io.hotmoka.node.remote.api.RemoteNode; import io.hotmoka.node.service.NodeServices; import io.hotmoka.verification.VerificationException; @@ -219,25 +217,6 @@ void testRemoteGetPolledResponse() throws Exception { } } - @Test - @DisplayName("starts a network server from a Hotmoka node and makes a remote call to getPolledResponse for a non-existing reference") - void testRemoteGetPolledResponseNonExisting() { - try (var service = NodeServices.of(node, PORT); var remote = RemoteNodes.of(URI, 50_000)) { - remote.getPolledResponse(INEXISTENT_TRANSACTION_REFERENCE); - } - catch (Exception e) { - assertTrue(e instanceof TimeoutException); - - // for remote nodes, the communication might time-out before polling time-outs - if (!(node instanceof RemoteNode)) - assertTrue(e.getMessage().contains("Cannot find the response of transaction reference 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")); - - return; - } - - fail("expected exception"); - } - @Test @DisplayName("starts a network server from a Hotmoka node and makes a remote call to getPolledResponse for the reference of a failed request") void testRemoteGetPolledResponseFailed() throws Exception {