diff --git a/io-hotmoka-node-local-api/src/main/java/io/hotmoka/node/local/api/NodeCache.java b/io-hotmoka-node-local-api/src/main/java/io/hotmoka/node/local/api/NodeCache.java index 5c21fea6b..d2fb7fdc9 100644 --- a/io-hotmoka-node-local-api/src/main/java/io/hotmoka/node/local/api/NodeCache.java +++ b/io-hotmoka-node-local-api/src/main/java/io/hotmoka/node/local/api/NodeCache.java @@ -19,11 +19,8 @@ import java.math.BigInteger; import java.util.Optional; -import io.hotmoka.node.api.NodeException; import io.hotmoka.node.api.nodes.ConsensusConfig; import io.hotmoka.node.api.responses.TransactionResponse; -import io.hotmoka.node.api.transactions.TransactionReference; -import io.hotmoka.node.api.values.StorageReference; import io.hotmoka.stores.EngineClassLoader; /** @@ -46,18 +43,6 @@ public interface NodeCache { */ void recomputeConsensus(); - /** - * Yields the response generated for the request for the given transaction. - * If this node has some form of commit, then this method succeeds - * also if the transaction has not been committed yet in the node. - * Nodes are allowed to keep in store all, some or none of the responses - * that they computed during their lifetime. - * - * @param reference the reference of the transaction - * @return the response, if any - */ - Optional getResponseUncommitted(TransactionReference reference); - /** * Yields the consensus parameters of the node. * @@ -65,43 +50,6 @@ public interface NodeCache { */ ConsensusConfig getConsensusParams(); - /** - * Yields the reference to the gamete account of the node. - * This method uses a cache to avoid repeated computations. - * - * @return the reference to the gamete account, if the node is already initialized - * @throws NodeException if the node is not able to complete the operation - */ - Optional getGamete() throws NodeException; - - /** - * Yields the reference to the contract that collects the validators of the node. - * After each transaction that consumes gas, the price of the gas is sent to this - * contract, that can later redistribute the reward to all validators. - * This method uses a cache to avoid repeated computations. - * - * @return the reference to the contract, if the node is already initialized - * @throws NodeException if the node is not able to complete the operation - */ - Optional getValidatorsUncommitted() throws NodeException; - - /** - * Yields the reference to the objects that keeps track of the - * versions of the modules of the node. - * - * @return the reference to the object, if the node is already initialized - * @throws NodeException if the node is not able to complete the operation - */ - Optional getVersionsUncommitted() throws NodeException; - - /** - * Yields the reference to the contract that keeps track of the gas cost. - * - * @return the reference to the contract, if the node is already initialized - * @throws NodeException if the node is not able to complete the operation - */ - Optional getGasStationUncommitted() throws NodeException; - /** * Yields the current gas price of the node. * diff --git a/io-hotmoka-node-local-api/src/main/java/io/hotmoka/node/local/api/StoreUtility.java b/io-hotmoka-node-local-api/src/main/java/io/hotmoka/node/local/api/StoreUtility.java deleted file mode 100644 index 4127cacb8..000000000 --- a/io-hotmoka-node-local-api/src/main/java/io/hotmoka/node/local/api/StoreUtility.java +++ /dev/null @@ -1,125 +0,0 @@ -/* -Copyright 2021 Fausto Spoto - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package io.hotmoka.node.local.api; - -import java.math.BigInteger; -import java.util.NoSuchElementException; -import java.util.Optional; - -import io.hotmoka.node.api.updates.ClassTag; -import io.hotmoka.node.api.values.StorageReference; -import io.hotmoka.stores.StoreException; - -/** - * An object that provides methods for reconstructing data from the store of a node. - * Most methods refer to the uncommitted store, that is, to the store including - * previous transactions that have not been committed yet. - * Others refer to the committed state instead. If the node has no notion of commit, - * the semantics of both kinds of methods coincide. - */ -public interface StoreUtility { - - /** - * Determines if the node is initialized, that is, its manifest has been set, - * although possibly not yet committed. - * - * @return true if and only if that condition holds - * @throws StoreException if the store is not able to complete the operation - */ - boolean nodeIsInitializedUncommitted() throws StoreException; - - /** - * Yields the gas station inside the manifest of the node, if the latter is already initialized. - * - * @return the gas station, if any - * @throws StoreException if the store is not able to complete the operation - */ - Optional getGasStationUncommitted() throws StoreException; - - /** - * Yields the validators contract inside the manifest of the node, if the latter is already initialized. - * - * @return the validators contract, if any - * @throws StoreException if the store is not able to complete the operation - */ - Optional getValidatorsUncommitted() throws StoreException; - - /** - * Yields the versions contract inside the manifest of the node, if the latter is already initialized. - * - * @return the versions contract, if any - * @throws StoreException if the store is not able to complete the operation - */ - Optional getVersionsUncommitted() throws StoreException; - - /** - * Yields the gamete account of the node, if the latter is already initialized. - * - * @return the gamete account, if any - * @throws StoreException if the store is not able to complete the operation - */ - Optional getGameteUncommitted() throws StoreException; - - /** - * Yields the Base64-encoded public key of the given account. - * - * @param account the account - * @return the public key - */ - String getPublicKeyUncommitted(StorageReference account); - - /** - * Yields the creator of the given event. - * - * @param event the event - * @return the reference to the creator - */ - StorageReference getCreatorUncommitted(StorageReference event); - - /** - * Yields the nonce of the given externally owned account. - * - * @param account the account - * @return the nonce - */ - BigInteger getNonceUncommitted(StorageReference account); - - /** - * Yields the current supply of coins of the given validators object. - * - * @param validators the validators object - * @return the current supply - */ - BigInteger getCurrentSupplyUncommitted(StorageReference validators); - - /** - * Yields the class name of the given object, whose creation might not be committed yet. - * - * @param object the object - * @return the class name - */ - String getClassNameUncommitted(StorageReference object); - - /** - * Yields the class tag of the given object, whose creation might not be committed yet. - * - * @param object the object - * @return the class tag - * @throws NoSuchElementException if {@code object} does not exist - */ - ClassTag getClassTagUncommitted(StorageReference object) throws NoSuchElementException; -} \ No newline at end of file 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 4ff0f6453..6a53dcad9 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 @@ -106,7 +106,6 @@ import io.hotmoka.node.local.api.LocalNodeConfig; import io.hotmoka.node.local.api.NodeCache; import io.hotmoka.node.local.api.ResponseBuilder; -import io.hotmoka.node.local.api.StoreUtility; import io.hotmoka.node.local.internal.transactions.ConstructorCallResponseBuilder; import io.hotmoka.node.local.internal.transactions.GameteCreationResponseBuilder; import io.hotmoka.node.local.internal.transactions.InitializationResponseBuilder; @@ -179,11 +178,6 @@ public final Hasher> getHasher() { return hasher; } - /** - * An object that provides utility methods on {@link #store}. - */ - protected final StoreUtility storeUtilities; - /** * The caches of the node. */ @@ -290,8 +284,7 @@ private AbstractLocalNodeImpl(C config, ConsensusConfig consensus, boolean throw new RuntimeException("Unexpected exception", e); } - this.storeUtilities = new StoreUtilityImpl(this); - this.caches = new NodeCachesImpl(this, consensus, config.getResponseCacheSize()); + this.caches = new NodeCachesImpl(this, consensus); this.recentCheckTransactionErrors = new LRUCache<>(100, 1000); this.gasConsumedSinceLastReward = ZERO; this.coinsSinceLastReward = ZERO; @@ -482,22 +475,22 @@ public final TransactionResponse getResponse(TransactionReference reference) thr } } - public Optional getRecentCheckTransactionErrorFor(TransactionReference reference) { - return Optional.ofNullable(recentCheckTransactionErrors.get(reference)); - } - @Override public final ClassTag getClassTag(StorageReference reference) throws UnknownReferenceException, NodeException { try (var scope = mkScope()) { Objects.requireNonNull(reference); - if (isNotCommitted(reference.getTransaction())) + var maybeResponse = store.getResponse(reference.getTransaction()); + if (maybeResponse.isEmpty()) throw new UnknownReferenceException(reference); - - return storeUtilities.getClassTagUncommitted(reference); - } - catch (NoSuchElementException e) { - throw new UnknownReferenceException(reference); + else if (maybeResponse.get() instanceof TransactionResponseWithUpdates trwu) + return trwu.getUpdates() + .filter(update -> update instanceof ClassTag && update.getObject().equals(reference)) + .map(update -> (ClassTag) update) + .findFirst() + .orElseThrow(() -> new NodeException("Object " + reference + " has not class tag in store")); + else + throw new NodeException("The creation of object " + reference + " does not contain updates"); } } @@ -506,10 +499,13 @@ public final Stream getState(StorageReference reference) throws UnknownR try (var scope = mkScope()) { Objects.requireNonNull(reference); try { - if (isNotCommitted(reference.getTransaction())) + if (isNotCommitted(reference.getTransaction())) // TODO: remove after making history optional throw new UnknownReferenceException(reference); - return getStateCommitted(reference); + Stream history = store.getHistory(reference); + var updates = new HashSet(); + CheckRunnable.check(StoreException.class, () -> history.forEachOrdered(UncheckConsumer.uncheck(transaction -> addUpdatesCommitted(reference, transaction, updates)))); + return updates.stream(); } catch (NoSuchElementException e) { throw new UnknownReferenceException(reference); @@ -524,11 +520,8 @@ public final Stream getState(StorageReference reference) throws UnknownR } } - private Stream getStateCommitted(StorageReference object) throws StoreException { - Stream history = store.getHistory(object); - var updates = new HashSet(); - CheckRunnable.check(StoreException.class, () -> history.forEachOrdered(UncheckConsumer.uncheck(transaction -> addUpdatesCommitted(object, transaction, updates)))); - return updates.stream(); + private Optional getRecentCheckTransactionErrorFor(TransactionReference reference) { + return Optional.ofNullable(recentCheckTransactionErrors.get(reference)); } /** @@ -790,10 +783,12 @@ public final boolean rewardValidators(String behaving, String misbehaving) { try { Optional manifest = getStoreTransaction().getManifestUncommitted(); if (manifest.isPresent()) { + var storeTransaction = getStoreTransaction(); + // we use the manifest as caller, since it is an externally-owned account StorageReference caller = manifest.get(); - BigInteger nonce = storeUtilities.getNonceUncommitted(caller); - StorageReference validators = caches.getValidatorsUncommitted().get(); // ok, since the manifest is present + BigInteger nonce = storeTransaction.getNonceUncommitted(caller); + StorageReference validators = storeTransaction.getValidatorsUncommitted().get(); // ok, since the manifest is present TransactionReference takamakaCode = validators.getTransaction(); // TODO: refer to getTakamakaCodeUncommitted() of the store transaction later // we determine how many coins have been minted during the last reward: @@ -804,7 +799,7 @@ public final boolean rewardValidators(String behaving, String misbehaving) { // as final supply: in that case we truncate the minted coins so that the current // supply reaches the final supply, exactly; this might occur from below (positive inflation) // or from above (negative inflation) - BigInteger currentSupply = storeUtilities.getCurrentSupplyUncommitted(validators); + BigInteger currentSupply = storeTransaction.getCurrentSupplyUncommitted(validators); if (minted.signum() > 0) { BigInteger finalSupply = caches.getConsensusParams().getFinalSupply(); BigInteger extra = finalSupply.subtract(currentSupply.add(minted)); @@ -993,10 +988,6 @@ Optional getManifestUncommitted() throws StoreException { return store.getManifest(); } - public StoreUtility getStoreUtilities() { - return storeUtilities; - } - public Future submit(Callable task) { return executors.submit(task); } 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 e718444cd..b513698ac 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 @@ -25,7 +25,6 @@ import java.security.spec.InvalidKeySpecException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.Objects; import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; @@ -34,7 +33,6 @@ import io.hotmoka.crypto.Base64; import io.hotmoka.crypto.Base64ConversionException; import io.hotmoka.crypto.SignatureAlgorithms; -import io.hotmoka.exceptions.UncheckSupplier; import io.hotmoka.node.MethodSignatures; import io.hotmoka.node.StorageTypes; import io.hotmoka.node.TransactionRequests; @@ -48,7 +46,6 @@ import io.hotmoka.node.api.responses.TransactionResponse; import io.hotmoka.node.api.responses.TransactionResponseWithEvents; import io.hotmoka.node.api.transactions.TransactionReference; -import io.hotmoka.node.api.updates.ClassTag; import io.hotmoka.node.api.values.BigIntegerValue; import io.hotmoka.node.api.values.BooleanValue; import io.hotmoka.node.api.values.IntValue; @@ -68,11 +65,6 @@ public class NodeCachesImpl implements NodeCache { private final AbstractLocalNodeImpl node; - /** - * The cache for the committed responses. - */ - private final LRUCache responses; - /** * The cache for the class loaders. */ @@ -83,28 +75,6 @@ public class NodeCachesImpl implements NodeCache { */ private volatile ConsensusConfig consensus; - /** - * The reference to the gamete account of the node. - */ - private volatile Optional gamete; - - /** - * The reference to the contract that manages the validators of the node. - * After each transaction that consumes gas, this contract receives the - * price of the gas, that can later be redistributed to the validators. - */ - private volatile Optional validators; - - /** - * The reference to the object that manages the versions of the modules of the node. - */ - private volatile Optional versions; - - /** - * The reference to the object that computes the cost of the gas. - */ - private volatile Optional gasStation; - /** * A cache for the current gas price. It gets reset if it changes. */ @@ -126,13 +96,8 @@ public class NodeCachesImpl implements NodeCache { * @param node the node * @param consensus the consensus parameters of the node */ - public NodeCachesImpl(AbstractLocalNodeImpl node, ConsensusConfig consensus, int responseCacheSize) { + public NodeCachesImpl(AbstractLocalNodeImpl node, ConsensusConfig consensus) { this.node = node; - this.responses = new LRUCache<>(100, responseCacheSize); - this.validators = Optional.empty(); - this.versions = Optional.empty(); - this.gasStation = Optional.empty(); - this.gamete = Optional.empty(); this.consensus = consensus; } @@ -163,19 +128,13 @@ public final void invalidateIfNeeded(TransactionResponse response, EngineClassLo } } - private Optional getTakamakaCodeUncommitted() throws StoreException { - return node.getManifestUncommitted() - .map(node.getStoreUtilities()::getClassTagUncommitted) - .map(ClassTag::getJar); - } - @Override public final void recomputeConsensus() { try { - StorageReference gasStation = getGasStationUncommitted().get(); - StorageReference validators = getValidatorsUncommitted().get(); - StorageReference versions = getVersionsUncommitted().get(); - TransactionReference takamakaCode = getTakamakaCodeUncommitted().get(); + StorageReference gasStation = node.getStoreTransaction().getGasStationUncommitted().get(); + StorageReference validators = node.getStoreTransaction().getValidatorsUncommitted().get(); + StorageReference versions = node.getStoreTransaction().getVersionsUncommitted().get(); + TransactionReference takamakaCode = node.getStoreTransaction().getTakamakaCodeUncommitted().get(); StorageReference manifest = node.getManifestUncommitted().get(); String genesisTime = ((StringValue) node.runInstanceMethodCallTransaction(TransactionRequests.instanceViewMethodCall @@ -317,72 +276,11 @@ public final void recomputeConsensus() { } } - private Optional getResponse(TransactionReference reference) { - return responses.computeIfAbsentOptional(Objects.requireNonNull(reference), _reference -> node.getStore().getResponse(_reference)); - } - - @Override - public final Optional getResponseUncommitted(TransactionReference reference) { - return getResponse(reference).or(UncheckSupplier.uncheck(() -> node.getResponseUncommitted(reference))); // TODO: recheck - } - @Override public final ConsensusConfig getConsensusParams() { return consensus; } - @Override - public final Optional getGamete() throws NodeException { - try { - if (gamete.isEmpty()) - gamete = node.getStoreUtilities().getGameteUncommitted(); - - return gamete; - } - catch (StoreException e) { - throw new NodeException(e); - } - } - - @Override - public final Optional getValidatorsUncommitted() throws NodeException { - try { - if (validators.isEmpty()) - validators = node.getStoreUtilities().getValidatorsUncommitted(); - - return validators; - } - catch (StoreException e) { - throw new NodeException(e); - } - } - - @Override - public final Optional getVersionsUncommitted() throws NodeException { - try { - if (versions.isEmpty()) - versions = node.getStoreUtilities().getVersionsUncommitted(); - - return versions; - } - catch (StoreException e) { - throw new NodeException(e); - } - } - - @Override - public final Optional getGasStationUncommitted() throws NodeException { - try { - if (gasStation.isEmpty()) - gasStation = node.getStoreUtilities().getGasStationUncommitted(); - - return gasStation; - } - catch (StoreException e) { - throw new NodeException(e); - } - } - @Override public final Optional getGasPrice() { if (gasPrice == null) @@ -401,11 +299,11 @@ public final Optional getCurrentInflation() { private void recomputeGasPrice() { try { - Optional manifest = node.getManifestUncommitted(); + Optional manifest = node.getStoreTransaction().getManifestUncommitted(); if (manifest.isPresent()) gasPrice = ((BigIntegerValue) node.runInstanceMethodCallTransaction(TransactionRequests.instanceViewMethodCall - (manifest.get(), _100_000, getTakamakaCodeUncommitted().get(), - MethodSignatures.GET_GAS_PRICE, getGasStationUncommitted().get())) + (manifest.get(), _100_000, node.getStoreTransaction().getTakamakaCodeUncommitted().get(), + MethodSignatures.GET_GAS_PRICE, node.getStoreTransaction().getGasStationUncommitted().get())) .orElseThrow(() -> new NodeException(MethodSignatures.GET_GAS_PRICE + " should not return void"))).getValue(); } catch (TransactionRejectedException | TransactionException | CodeExecutionException | StoreException | NodeException e) { @@ -418,8 +316,8 @@ private void recomputeInflation() { Optional manifest = node.getManifestUncommitted(); if (manifest.isPresent()) inflation = ((LongValue) node.runInstanceMethodCallTransaction(TransactionRequests.instanceViewMethodCall - (manifest.get(), _100_000, getTakamakaCodeUncommitted().get(), - MethodSignatures.GET_CURRENT_INFLATION, getValidatorsUncommitted().get())) + (manifest.get(), _100_000, node.getStoreTransaction().getTakamakaCodeUncommitted().get(), + MethodSignatures.GET_CURRENT_INFLATION, node.getStoreTransaction().getValidatorsUncommitted().get())) .orElseThrow(() -> new NodeException(MethodSignatures.GET_CURRENT_INFLATION + " should not return void"))).getValue(); } catch (TransactionRejectedException | TransactionException | CodeExecutionException | StoreException | NodeException e) { @@ -445,19 +343,19 @@ private boolean consensusParametersMightHaveChanged(TransactionResponse response try { if (isInitializedUncommitted() && response instanceof TransactionResponseWithEvents trwe) { Stream events = trwe.getEvents(); - StorageReference manifest = node.getManifestUncommitted().get(); - StorageReference gasStation = getGasStationUncommitted().get(); - StorageReference versions = getVersionsUncommitted().get(); - StorageReference validators = getValidatorsUncommitted().get(); + StorageReference manifest = node.getStoreTransaction().getManifestUncommitted().get(); + StorageReference gasStation = node.getStoreTransaction().getGasStationUncommitted().get(); + StorageReference versions = node.getStoreTransaction().getVersionsUncommitted().get(); + StorageReference validators = node.getStoreTransaction().getValidatorsUncommitted().get(); return check(ClassNotFoundException.class, () -> events.filter(uncheck(event -> isConsensusUpdateEvent(event, classLoader))) - .map(node.getStoreUtilities()::getCreatorUncommitted) + .map(node.getStoreTransaction()::getCreatorUncommitted) .anyMatch(creator -> creator.equals(manifest) || creator.equals(validators) || creator.equals(gasStation) || creator.equals(versions)) ); } } - catch (StoreException | NodeException e) { + catch (StoreException e) { logger.log(Level.SEVERE, "cannot check the consensus parameters", e); } @@ -476,7 +374,7 @@ private boolean isInitializedUncommitted() throws StoreException { } private boolean isConsensusUpdateEvent(StorageReference event, EngineClassLoader classLoader) throws ClassNotFoundException { - return classLoader.isConsensusUpdateEvent(node.getStoreUtilities().getClassNameUncommitted(event)); + return classLoader.isConsensusUpdateEvent(node.getStoreTransaction().getClassNameUncommitted(event)); } /** @@ -495,16 +393,16 @@ private boolean gasPriceMightHaveChanged(TransactionResponse response, EngineCla // we check if there are events of type GasPriceUpdate triggered by the gas station if (isInitializedUncommitted() && response instanceof TransactionResponseWithEvents trwe) { Stream events = trwe.getEvents(); - StorageReference gasStation = getGasStationUncommitted().get(); + StorageReference gasStation = node.getStoreTransaction().getGasStationUncommitted().get(); return check(ClassNotFoundException.class, () -> events.filter(uncheck(event -> isGasPriceUpdateEvent(event, classLoader))) - .map(node.getStoreUtilities()::getCreatorUncommitted) + .map(node.getStoreTransaction()::getCreatorUncommitted) .anyMatch(gasStation::equals) ); } } - catch (StoreException | NodeException e) { + catch (StoreException e) { logger.log(Level.SEVERE, "cannot check the gas price", e); } @@ -527,16 +425,16 @@ private boolean inflationMightHaveChanged(TransactionResponse response, EngineCl // we check if there are events of type InflationUpdate triggered by the validators object if (isInitializedUncommitted() && response instanceof TransactionResponseWithEvents) { Stream events = ((TransactionResponseWithEvents) response).getEvents(); - StorageReference validators = getValidatorsUncommitted().get(); + StorageReference validators = node.getStoreTransaction().getValidatorsUncommitted().get(); return check(ClassNotFoundException.class, () -> events.filter(uncheck(event -> isInflationUpdateEvent(event, classLoader))) - .map(node.getStoreUtilities()::getCreatorUncommitted) + .map(node.getStoreTransaction()::getCreatorUncommitted) .anyMatch(validators::equals) ); } } - catch (StoreException | NodeException e) { + catch (StoreException e) { logger.log(Level.SEVERE, "cannot check the inflation", e); } @@ -544,10 +442,10 @@ private boolean inflationMightHaveChanged(TransactionResponse response, EngineCl } private boolean isGasPriceUpdateEvent(StorageReference event, EngineClassLoader classLoader) throws ClassNotFoundException { - return classLoader.isGasPriceUpdateEvent(node.getStoreUtilities().getClassNameUncommitted(event)); + return classLoader.isGasPriceUpdateEvent(node.getStoreTransaction().getClassNameUncommitted(event)); } private boolean isInflationUpdateEvent(StorageReference event, EngineClassLoader classLoader) throws ClassNotFoundException { - return classLoader.isInflationUpdateEvent(node.getStoreUtilities().getClassNameUncommitted(event)); + return classLoader.isInflationUpdateEvent(node.getStoreTransaction().getClassNameUncommitted(event)); } } \ No newline at end of file diff --git a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/NonInitialResponseBuilderImpl.java b/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/NonInitialResponseBuilderImpl.java index 6e0802cae..e762c107e 100644 --- a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/NonInitialResponseBuilderImpl.java +++ b/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/NonInitialResponseBuilderImpl.java @@ -208,10 +208,15 @@ else if (classLoader.getAccountQTESLA3().isAssignableFrom(clazz)) * @throws UnknownReferenceException */ private void callerMustBeExternallyOwnedAccount() throws TransactionRejectedException, ClassNotFoundException, NodeException, UnknownReferenceException { - ClassTag classTag = node.getClassTag(request.getCaller()); - Class clazz = classLoader.loadClass(classTag.getClazz().getName()); - if (!classLoader.getExternallyOwnedAccount().isAssignableFrom(clazz)) - throw new TransactionRejectedException("the caller of a request must be an externally owned account"); + try { + ClassTag classTag = storeTransaction.getClassTagUncommitted(request.getCaller()); + Class clazz = classLoader.loadClass(classTag.getClazz().getName()); + if (!classLoader.getExternallyOwnedAccount().isAssignableFrom(clazz)) + throw new TransactionRejectedException("the caller of a request must be an externally owned account"); + } + catch (NoSuchElementException e) { + throw new UnknownReferenceException(e); + } } /** @@ -233,7 +238,7 @@ private void payerMustBeContract() throws TransactionRejectedException, ClassNot return; // otherwise we check - ClassTag classTag = node.getClassTag(payer); + ClassTag classTag = storeTransaction.getClassTagUncommitted(payer); Class clazz = classLoader.loadClass(classTag.getClazz().getName()); if (!classLoader.getContract().isAssignableFrom(clazz)) throw new TransactionRejectedException("the payer of a request must be a contract"); @@ -442,7 +447,13 @@ protected ResponseCreator() throws TransactionRejectedException { protected final void init() throws NodeException { this.deserializedCaller = deserializer.deserialize(request.getCaller()); this.deserializedPayer = deserializedPayer(); - this.deserializedValidators = node.caches.getValidatorsUncommitted().map(deserializer::deserialize); + + try { + this.deserializedValidators = storeTransaction.getValidatorsUncommitted().map(deserializer::deserialize); + } + catch (StoreException e) { + throw new NodeException(e); + } increaseNonceOfCaller(); chargeGasForCPU(gasCostModel.cpuBaseTransactionCost()); 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 deleted file mode 100644 index e6a784a58..000000000 --- a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/StoreUtilityImpl.java +++ /dev/null @@ -1,175 +0,0 @@ -/* -Copyright 2021 Fausto Spoto - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package io.hotmoka.node.local.internal; - -import java.math.BigInteger; -import java.util.NoSuchElementException; -import java.util.Optional; - -import io.hotmoka.node.FieldSignatures; -import io.hotmoka.node.api.responses.TransactionResponse; -import io.hotmoka.node.api.responses.TransactionResponseWithUpdates; -import io.hotmoka.node.api.signatures.FieldSignature; -import io.hotmoka.node.api.transactions.TransactionReference; -import io.hotmoka.node.api.updates.ClassTag; -import io.hotmoka.node.api.updates.UpdateOfField; -import io.hotmoka.node.api.values.BigIntegerValue; -import io.hotmoka.node.api.values.StorageReference; -import io.hotmoka.node.api.values.StringValue; -import io.hotmoka.node.local.api.StoreUtility; -import io.hotmoka.stores.StoreException; - -/** - * The implementation of an object that provides methods for reconstructing data from the store of a node. - */ -public class StoreUtilityImpl implements StoreUtility { - - /** - * The node whose store is accessed. - */ - private final AbstractLocalNodeImpl node; - - /** - * Builds an object that provides utility methods on the store of a node. - * - * @param node the node whose store is accessed - */ - public StoreUtilityImpl(AbstractLocalNodeImpl node) { - this.node = node; - } - - @Override - public boolean nodeIsInitializedUncommitted() throws StoreException { - return node.getManifestUncommitted().isPresent(); - } - - @Override - public Optional getGasStationUncommitted() throws StoreException { - return node.getManifestUncommitted().map(_manifest -> getReferenceFieldUncommitted(_manifest, FieldSignatures.MANIFEST_GAS_STATION_FIELD)); - } - - @Override - public Optional getValidatorsUncommitted() throws StoreException { - return node.getManifestUncommitted().map(_manifest -> getReferenceFieldUncommitted(_manifest, FieldSignatures.MANIFEST_VALIDATORS_FIELD)); - } - - @Override - public Optional getGameteUncommitted() throws StoreException { - return node.getManifestUncommitted().map(_manifest -> getReferenceFieldUncommitted(_manifest, FieldSignatures.MANIFEST_GAMETE_FIELD)); - } - - @Override - public Optional getVersionsUncommitted() throws StoreException { - return node.getManifestUncommitted().map(_manifest -> getReferenceFieldUncommitted(_manifest, FieldSignatures.MANIFEST_VERSIONS_FIELD)); - } - - @Override - public BigInteger getCurrentSupplyUncommitted(StorageReference validators) { - return getBigIntegerFieldUncommitted(validators, FieldSignatures.ABSTRACT_VALIDATORS_CURRENT_SUPPLY_FIELD); - } - - @Override - public String getPublicKeyUncommitted(StorageReference account) { - return getStringFieldUncommitted(account, FieldSignatures.EOA_PUBLIC_KEY_FIELD); - } - - @Override - public StorageReference getCreatorUncommitted(StorageReference event) { - return getReferenceFieldUncommitted(event, FieldSignatures.EVENT_CREATOR_FIELD); - } - - @Override - public BigInteger getNonceUncommitted(StorageReference account) { - return getBigIntegerFieldUncommitted(account, FieldSignatures.EOA_NONCE_FIELD); - } - - @Override - public String getClassNameUncommitted(StorageReference reference) { - return getClassTagUncommitted(reference).getClazz().getName(); - } - - @Override - public ClassTag getClassTagUncommitted(StorageReference reference) throws NoSuchElementException { - // we go straight to the transaction that created the object - return node.caches.getResponseUncommitted(reference.getTransaction()) - .filter(response -> response instanceof TransactionResponseWithUpdates) - .flatMap(response -> ((TransactionResponseWithUpdates) response).getUpdates() - .filter(update -> update instanceof ClassTag && update.getObject().equals(reference)) - .map(update -> (ClassTag) update) - .findFirst()) - .orElseThrow(() -> new NoSuchElementException("Object " + reference + " does not exist")); - } - - private Optional getLastUpdateToFieldUncommitted(StorageReference object, FieldSignature field) throws StoreException { - return node.getHistoryUncommitted(object) - .map(transaction -> getLastUpdateUncommitted(object, field, transaction)) - .filter(Optional::isPresent) - .map(Optional::get) - .findFirst(); - } - - private StorageReference getReferenceFieldUncommitted(StorageReference object, FieldSignature field) { - try { - return (StorageReference) getLastUpdateToFieldUncommitted(object, field).get().getValue(); - } - catch (StoreException e) { - throw new RuntimeException(e); // TODO - } - } - - private BigInteger getBigIntegerFieldUncommitted(StorageReference object, FieldSignature field) { - try { - return ((BigIntegerValue) getLastUpdateToFieldUncommitted(object, field).get().getValue()).getValue(); - } - catch (StoreException e) { - throw new RuntimeException(e); // TODO - } - } - - private String getStringFieldUncommitted(StorageReference object, FieldSignature field) { - try { - return ((StringValue) getLastUpdateToFieldUncommitted(object, field).get().getValue()).getValue(); - } - catch (StoreException e) { - throw new RuntimeException(e); // TODO - } - } - - /** - * Yields the update to the given field of the object at the given reference, - * generated during a given transaction. - * - * @param object the reference of the object - * @param field the field of the object - * @param transaction the reference to the transaction - * @return the update, if any. If the field of {@code object} was not modified during - * the {@code transaction}, this method returns an empty optional - */ - private Optional getLastUpdateUncommitted(StorageReference object, FieldSignature field, TransactionReference transaction) { - TransactionResponse response = node.caches.getResponseUncommitted(transaction) - .orElseThrow(() -> new RuntimeException("Unknown transaction reference " + transaction)); - - if (response instanceof TransactionResponseWithUpdates trwu) - return trwu.getUpdates() - .filter(update -> update instanceof UpdateOfField) - .map(update -> (UpdateOfField) update) - .filter(update -> update.getObject().equals(object) && update.getField().equals(field)) - .findFirst(); - else - throw new RuntimeException("Transaction reference " + transaction + " does not contain updates"); - } -} \ No newline at end of file diff --git a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/transactions/CodeCallResponseBuilder.java b/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/transactions/CodeCallResponseBuilder.java index 6175c5cfa..bcb0432d4 100644 --- a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/transactions/CodeCallResponseBuilder.java +++ b/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/transactions/CodeCallResponseBuilder.java @@ -107,9 +107,14 @@ private void argumentsAreExported() throws TransactionRejectedException, ClassNo * @throws UnknownReferenceException */ protected final void enforceExported(StorageReference reference) throws TransactionRejectedException, ClassNotFoundException, NodeException, UnknownReferenceException { - var clazz = node.getClassTag(reference).getClazz(); - if (!classLoader.isExported(clazz.getName())) - throw new TransactionRejectedException("cannot pass as argument a value of the non-exported type " + clazz); + try { + var clazz = storeTransaction.getClassTagUncommitted(reference).getClazz(); + if (!classLoader.isExported(clazz.getName())) + throw new TransactionRejectedException("cannot pass as argument a value of the non-exported type " + clazz); + } + catch (NoSuchElementException e) { + throw new UnknownReferenceException(e); + } } /** diff --git a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/transactions/InstanceMethodCallResponseBuilder.java b/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/transactions/InstanceMethodCallResponseBuilder.java index 2a7094bf3..a9f3c3f3f 100644 --- a/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/transactions/InstanceMethodCallResponseBuilder.java +++ b/io-hotmoka-node-local/src/main/java/io/hotmoka/node/local/internal/transactions/InstanceMethodCallResponseBuilder.java @@ -94,9 +94,9 @@ protected boolean transactionIsSigned() { private boolean callerIsGameteOfTheNode() { try { - return node.caches.getGamete().filter(request.getCaller()::equals).isPresent(); + return storeTransaction.getGameteUncommitted().filter(request.getCaller()::equals).isPresent(); } - catch (NodeException e) { + catch (StoreException e) { LOGGER.log(Level.SEVERE, "", e); return false; } 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 5c3e08b23..8b07d5aef 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 @@ -249,7 +249,7 @@ protected ResponseBeginBlock beginBlock(RequestBeginBlock request) { throw new RuntimeException(e); // TODO } - node.transaction = transaction; + node.storeTransaction = transaction; logger.info("validators reward: behaving: " + behaving + ", misbehaving: " + misbehaving); node.rewardValidators(behaving, misbehaving); @@ -307,7 +307,7 @@ protected ResponseEndBlock endBlock(RequestEndBlock request) { } } } - catch (TransactionRejectedException | TransactionException | CodeExecutionException | NoSuchElementException | NodeException e) { + catch (TransactionRejectedException | TransactionException | CodeExecutionException | NoSuchElementException | StoreException | NodeException e) { throw new RuntimeException("could not determine the new validators set", e); } } 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 16dc10718..fc89bdd23 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 @@ -104,7 +104,7 @@ public class TendermintNodeImpl extends AbstractLocalNode transaction; + public volatile StoreTransaction storeTransaction; /** * Builds a brand new Tendermint blockchain. This constructor spawns the Tendermint process on localhost @@ -256,7 +256,7 @@ protected void invalidateCachesIfNeeded(TransactionResponse response, EngineClas private volatile TendermintValidator[] tendermintValidatorsCached; - Optional getTendermintValidatorsInStore() throws TransactionRejectedException, TransactionException, CodeExecutionException, NodeException { + Optional getTendermintValidatorsInStore() throws TransactionRejectedException, TransactionException, CodeExecutionException, NodeException, StoreException { if (tendermintValidatorsCached != null) return Optional.of(tendermintValidatorsCached); @@ -269,7 +269,7 @@ Optional getTendermintValidatorsInStore() throws Transact return Optional.empty(); } - StorageReference validators = caches.getValidatorsUncommitted().get(); // the manifest is already set + StorageReference validators = storeTransaction.getValidatorsUncommitted().get(); // the manifest is already set TransactionReference takamakaCode = getTakamakaCode(); var shares = (StorageReference) runInstanceMethodCallTransaction(TransactionRequests.instanceViewMethodCall @@ -295,7 +295,7 @@ Optional getTendermintValidatorsInStore() throws Transact (manifest, _50_000, takamakaCode, GET, shares, validator)) .orElseThrow(() -> new NodeException(GET + " should not return void"))).getValue().longValue(); - String publicKey = storeUtilities.getPublicKeyUncommitted(validator); + String publicKey = storeTransaction.getPublicKeyUncommitted(validator); result[num] = new TendermintValidator(id, power, publicKey, "tendermint/PubKeyEd25519"); } @@ -315,18 +315,18 @@ Optional getTendermintValidatorsInStore() throws Transact */ private boolean validatorsMightHaveChanged(TransactionResponse response, EngineClassLoader classLoader) throws ClassNotFoundException { try { - if (storeUtilities.nodeIsInitializedUncommitted() && response instanceof TransactionResponseWithEvents) { - Stream events = ((TransactionResponseWithEvents) response).getEvents(); - StorageReference validators = caches.getValidatorsUncommitted().get(); + if (storeTransaction.nodeIsInitializedUncommitted() && response instanceof TransactionResponseWithEvents trwe) { + Stream events = trwe.getEvents(); + StorageReference validators = storeTransaction.getValidatorsUncommitted().get(); return check(ClassNotFoundException.class, () -> events.filter(uncheck(event -> isValidatorsUpdateEvent(event, classLoader))) - .map(storeUtilities::getCreatorUncommitted) + .map(storeTransaction::getCreatorUncommitted) .anyMatch(validators::equals) ); } } - catch (StoreException | NodeException e) { + catch (StoreException e) { LOGGER.log(Level.SEVERE, "", e); } @@ -334,7 +334,7 @@ private boolean validatorsMightHaveChanged(TransactionResponse response, EngineC } private boolean isValidatorsUpdateEvent(StorageReference event, EngineClassLoader classLoader) throws ClassNotFoundException { - return classLoader.isValidatorsUpdateEvent(storeUtilities.getClassNameUncommitted(event)); + return classLoader.isValidatorsUpdateEvent(storeTransaction.getClassNameUncommitted(event)); } /** @@ -491,6 +491,6 @@ private void initWorkingDirectoryOfTendermintProcess(TendermintNodeConfig config @Override public StoreTransaction getStoreTransaction() { - return transaction; + return storeTransaction; } } \ No newline at end of file