diff --git a/src/main/java/com/iota/iri/Iota.java b/src/main/java/com/iota/iri/Iota.java index d104db0fa4..f054ab66eb 100644 --- a/src/main/java/com/iota/iri/Iota.java +++ b/src/main/java/com/iota/iri/Iota.java @@ -10,9 +10,17 @@ import com.iota.iri.network.UDPReceiver; import com.iota.iri.network.replicator.Replicator; import com.iota.iri.service.TipsSolidifier; +import com.iota.iri.service.ledger.impl.LedgerServiceImpl; +import com.iota.iri.service.milestone.impl.LatestMilestoneTrackerImpl; +import com.iota.iri.service.milestone.impl.LatestSolidMilestoneTrackerImpl; +import com.iota.iri.service.milestone.impl.MilestoneServiceImpl; +import com.iota.iri.service.milestone.impl.MilestoneSolidifierImpl; +import com.iota.iri.service.milestone.impl.SeenMilestonesRetrieverImpl; import com.iota.iri.service.snapshot.SnapshotException; import com.iota.iri.service.snapshot.SnapshotProvider; +import com.iota.iri.service.snapshot.impl.LocalSnapshotManagerImpl; import com.iota.iri.service.snapshot.impl.SnapshotProviderImpl; +import com.iota.iri.service.snapshot.impl.SnapshotServiceImpl; import com.iota.iri.service.tipselection.EntryPointSelector; import com.iota.iri.service.tipselection.RatingCalculator; import com.iota.iri.service.tipselection.TailFinder; @@ -23,6 +31,8 @@ import com.iota.iri.service.tipselection.impl.TailFinderImpl; import com.iota.iri.service.tipselection.impl.TipSelectorImpl; import com.iota.iri.service.tipselection.impl.WalkerAlpha; +import com.iota.iri.service.transactionpruning.TransactionPruningException; +import com.iota.iri.service.transactionpruning.async.AsyncTransactionPruner; import com.iota.iri.storage.Indexable; import com.iota.iri.storage.Persistable; import com.iota.iri.storage.PersistenceProvider; @@ -73,10 +83,27 @@ public class Iota { private static final Logger log = LoggerFactory.getLogger(Iota.class); - public final LedgerValidator ledgerValidator; - public final MilestoneTracker milestoneTracker; + public final SnapshotProviderImpl snapshotProvider; + + public final SnapshotServiceImpl snapshotService; + + public final LocalSnapshotManagerImpl localSnapshotManager; + + public final MilestoneServiceImpl milestoneService; + + public final LatestMilestoneTrackerImpl latestMilestoneTracker; + + public final LatestSolidMilestoneTrackerImpl latestSolidMilestoneTracker; + + public final SeenMilestonesRetrieverImpl seenMilestonesRetriever; + + public final LedgerServiceImpl ledgerService = new LedgerServiceImpl(); + + public final AsyncTransactionPruner transactionPruner; + + public final MilestoneSolidifierImpl milestoneSolidifier; + public final Tangle tangle; - public final SnapshotProvider snapshotProvider; public final TransactionValidator transactionValidator; public final TipsSolidifier tipsSolidifier; public final TransactionRequester transactionRequester; @@ -92,27 +119,38 @@ public class Iota { * Initializes the latest snapshot and then creates all services needed to run an IOTA node. * * @param configuration Information about how this node will be configured. - * @throws IOException If the Snapshot fails to initialize. - * This can happen if the snapshot signature is invalid or the file cannot be read. + * @throws TransactionPruningException If the TransactionPruner could not restore its state. + * @throws SnapshotException If the Snapshot fails to initialize. + * This can happen if the snapshot signature is invalid or the file cannot be read. */ - public Iota(IotaConfig configuration) throws SnapshotException, IOException { + public Iota(IotaConfig configuration) throws TransactionPruningException, SnapshotException { this.configuration = configuration; - Snapshot initialSnapshot = Snapshot.init(configuration).clone(); + + // new refactored instances + snapshotProvider = new SnapshotProviderImpl(); + snapshotService = new SnapshotServiceImpl(); + localSnapshotManager = new LocalSnapshotManagerImpl(); + milestoneService = new MilestoneServiceImpl(); + latestMilestoneTracker = new LatestMilestoneTrackerImpl(); + latestSolidMilestoneTracker = new LatestSolidMilestoneTrackerImpl(); + seenMilestonesRetriever = new SeenMilestonesRetrieverImpl(); + milestoneSolidifier = new MilestoneSolidifierImpl(); + transactionPruner = new AsyncTransactionPruner(); + + // legacy code tangle = new Tangle(); messageQ = MessageQ.createWith(configuration); tipsViewModel = new TipsViewModel(); - snapshotProvider = new SnapshotProviderImpl(configuration); - transactionRequester = new TransactionRequester(tangle, snapshotProvider.getInitialSnapshot(), messageQ); - transactionValidator = new TransactionValidator(tangle, snapshotProvider.getInitialSnapshot(), tipsViewModel, - transactionRequester); - milestoneTracker = new MilestoneTracker(tangle, snapshotProvider, transactionValidator, messageQ, initialSnapshot, configuration); - node = new Node(tangle, snapshotProvider.getInitialSnapshot(), transactionValidator, transactionRequester, tipsViewModel, milestoneTracker, messageQ, - configuration); + transactionRequester = new TransactionRequester(tangle, snapshotProvider, messageQ); + transactionValidator = new TransactionValidator(tangle, snapshotProvider, tipsViewModel, transactionRequester); + node = new Node(tangle, snapshotProvider, transactionValidator, transactionRequester, tipsViewModel, + latestMilestoneTracker, messageQ, configuration); replicator = new Replicator(node, configuration); udpReceiver = new UDPReceiver(node, configuration); - ledgerValidator = new LedgerValidator(tangle, snapshotProvider, milestoneTracker, transactionRequester, messageQ); tipsSolidifier = new TipsSolidifier(tangle, transactionValidator, tipsViewModel); tipsSelector = createTipSelector(configuration); + + injectDependencies(); } /** @@ -136,13 +174,40 @@ public void init() throws Exception { tangle.clearColumn(com.iota.iri.model.StateDiff.class); tangle.clearMetadata(com.iota.iri.model.persistables.Transaction.class); } - milestoneTracker.init(SpongeFactory.Mode.CURLP27, 1, ledgerValidator); + transactionValidator.init(configuration.isTestnet(), configuration.getMwm()); tipsSolidifier.init(); transactionRequester.init(configuration.getpRemoveRequest()); udpReceiver.init(); replicator.init(); node.init(); + + latestMilestoneTracker.start(); + latestSolidMilestoneTracker.start(); + seenMilestonesRetriever.start(); + milestoneSolidifier.start(); + + if (configuration.getLocalSnapshotsEnabled()) { + localSnapshotManager.start(latestMilestoneTracker); + + if (configuration.getLocalSnapshotsPruningEnabled()) { + transactionPruner.start(); + } + } + } + + private void injectDependencies() throws SnapshotException, TransactionPruningException { + snapshotProvider.init(configuration); + snapshotService.init(tangle, snapshotProvider, configuration); + localSnapshotManager.init(snapshotProvider, snapshotService, transactionPruner, configuration); + milestoneService.init(tangle, snapshotProvider, messageQ, configuration); + latestMilestoneTracker.init(tangle, snapshotProvider, milestoneService, milestoneSolidifier, + messageQ, configuration); + latestSolidMilestoneTracker.init(tangle, snapshotProvider, ledgerService, latestMilestoneTracker, messageQ); + seenMilestonesRetriever.init(tangle, snapshotProvider, transactionRequester); + milestoneSolidifier.init(snapshotProvider, transactionValidator); + ledgerService.init(tangle, snapshotProvider, snapshotService, milestoneService); + transactionPruner.init(tangle, snapshotProvider, tipsViewModel, configuration).restoreState(); } private void rescanDb() throws Exception { @@ -175,7 +240,6 @@ private void rescanDb() throws Exception { * Exceptions during shutdown are not caught. */ public void shutdown() throws Exception { - milestoneTracker.shutDown(); tipsSolidifier.shutdown(); node.shutdown(); udpReceiver.shutdown(); @@ -183,6 +247,21 @@ public void shutdown() throws Exception { transactionValidator.shutdown(); tangle.shutdown(); messageQ.shutdown(); + + // shutdown in reverse starting order (to not break any dependencies) + milestoneSolidifier.shutdown(); + seenMilestonesRetriever.shutdown(); + latestSolidMilestoneTracker.shutdown(); + latestMilestoneTracker.shutdown(); + snapshotProvider.shutdown(); + + if (configuration.getLocalSnapshotsEnabled()) { + localSnapshotManager.shutdown(); + + if (configuration.getLocalSnapshotsPruningEnabled()) { + transactionPruner.shutdown(); + } + } } private void initializeTangle() { @@ -204,11 +283,12 @@ private void initializeTangle() { } private TipSelector createTipSelector(TipSelConfig config) { - EntryPointSelector entryPointSelector = new EntryPointSelectorImpl(tangle, snapshotProvider, milestoneTracker); - RatingCalculator ratingCalculator = new CumulativeWeightCalculator(tangle, snapshotProvider.getInitialSnapshot()); + EntryPointSelector entryPointSelector = new EntryPointSelectorImpl(tangle, snapshotProvider, + latestMilestoneTracker); + RatingCalculator ratingCalculator = new CumulativeWeightCalculator(tangle, snapshotProvider); TailFinder tailFinder = new TailFinderImpl(tangle); Walker walker = new WalkerAlpha(tailFinder, tangle, messageQ, new SecureRandom(), config); - return new TipSelectorImpl(tangle, snapshotProvider, ledgerValidator, entryPointSelector, ratingCalculator, - walker, milestoneTracker, config); + return new TipSelectorImpl(tangle, snapshotProvider, ledgerService, entryPointSelector, ratingCalculator, + walker, config); } } diff --git a/src/main/java/com/iota/iri/TransactionValidator.java b/src/main/java/com/iota/iri/TransactionValidator.java index 9d1143e513..da8f9cb575 100644 --- a/src/main/java/com/iota/iri/TransactionValidator.java +++ b/src/main/java/com/iota/iri/TransactionValidator.java @@ -5,10 +5,10 @@ import com.iota.iri.crypto.Curl; import com.iota.iri.crypto.Sponge; import com.iota.iri.crypto.SpongeFactory; -import com.iota.iri.service.snapshot.Snapshot; import com.iota.iri.model.Hash; import com.iota.iri.model.TransactionHash; import com.iota.iri.network.TransactionRequester; +import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.storage.Tangle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,7 +24,7 @@ public class TransactionValidator { public static final int SOLID_SLEEP_TIME = 500; private final Tangle tangle; - private final Snapshot initialSnapshot; + private final SnapshotProvider snapshotProvider; private final TipsViewModel tipsViewModel; private final TransactionRequester transactionRequester; private int minWeightMagnitude = 81; @@ -55,13 +55,13 @@ public class TransactionValidator { * Constructor for Tangle Validator * * @param tangle relays tangle data to and from the persistence layer - * @param initialSnapshot the initial snapshot that defines the genesis for our ledger state + * @param snapshotProvider data provider for the snapshots that are relevant for the node * @param tipsViewModel container that gets updated with the latest tips (transactions with no children) * @param transactionRequester used to request missing transactions from neighbors */ - TransactionValidator(Tangle tangle, Snapshot initialSnapshot, TipsViewModel tipsViewModel, TransactionRequester transactionRequester) { + TransactionValidator(Tangle tangle, SnapshotProvider snapshotProvider, TipsViewModel tipsViewModel, TransactionRequester transactionRequester) { this.tangle = tangle; - this.initialSnapshot = initialSnapshot; + this.snapshotProvider = snapshotProvider; this.tipsViewModel = tipsViewModel; this.transactionRequester = transactionRequester; } @@ -135,10 +135,10 @@ private boolean hasInvalidTimestamp(TransactionViewModel transactionViewModel) { } if (transactionViewModel.getAttachmentTimestamp() == 0) { - return transactionViewModel.getTimestamp() < initialSnapshot.getTimestamp() && !initialSnapshot.hasSolidEntryPoint(transactionViewModel.getHash()) + return transactionViewModel.getTimestamp() < snapshotProvider.getInitialSnapshot().getTimestamp() && !snapshotProvider.getInitialSnapshot().hasSolidEntryPoint(transactionViewModel.getHash()) || transactionViewModel.getTimestamp() > (System.currentTimeMillis() / 1000) + MAX_TIMESTAMP_FUTURE; } - return transactionViewModel.getAttachmentTimestamp() < (initialSnapshot.getTimestamp() * 1000L) + return transactionViewModel.getAttachmentTimestamp() < (snapshotProvider.getInitialSnapshot().getTimestamp() * 1000L) || transactionViewModel.getAttachmentTimestamp() > System.currentTimeMillis() + MAX_TIMESTAMP_FUTURE_MS; } @@ -246,7 +246,7 @@ public boolean checkSolidity(Hash hash, boolean milestone, int maxProcessedTrans if(fromHash(tangle, hash).isSolid()) { return true; } - Set analyzedHashes = new HashSet<>(initialSnapshot.getSolidEntryPoints().keySet()); + Set analyzedHashes = new HashSet<>(snapshotProvider.getInitialSnapshot().getSolidEntryPoints().keySet()); if(maxProcessedTransactions != Integer.MAX_VALUE) { maxProcessedTransactions += analyzedHashes.size(); } @@ -261,7 +261,7 @@ public boolean checkSolidity(Hash hash, boolean milestone, int maxProcessedTrans final TransactionViewModel transaction = fromHash(tangle, hashPointer); if(!transaction.isSolid()) { - if (transaction.getType() == PREFILLED_SLOT && !initialSnapshot.hasSolidEntryPoint(hashPointer)) { + if (transaction.getType() == PREFILLED_SLOT && !snapshotProvider.getInitialSnapshot().hasSolidEntryPoint(hashPointer)) { solid = false; if (!transactionRequester.isTransactionRequested(hashPointer, milestone)) { @@ -276,7 +276,7 @@ public boolean checkSolidity(Hash hash, boolean milestone, int maxProcessedTrans } } if (solid) { - updateSolidTransactions(tangle, initialSnapshot, analyzedHashes); + updateSolidTransactions(tangle, snapshotProvider.getInitialSnapshot(), analyzedHashes); } analyzedHashes.clear(); return solid; @@ -339,7 +339,7 @@ void propagateSolidTransactions() { for(Hash h: approvers) { TransactionViewModel tx = fromHash(tangle, h); if(quietQuickSetSolid(tx)) { - tx.update(tangle, initialSnapshot, "solid|height"); + tx.update(tangle, snapshotProvider.getInitialSnapshot(), "solid|height"); tipsViewModel.setSolid(h); addSolidTransaction(h); } @@ -384,7 +384,7 @@ public void updateStatus(TransactionViewModel transactionViewModel) throws Excep tipsViewModel.removeTipHash(transactionViewModel.getBranchTransactionHash()); if(quickSetSolid(transactionViewModel)) { - transactionViewModel.update(tangle, initialSnapshot, "solid|height"); + transactionViewModel.update(tangle, snapshotProvider.getInitialSnapshot(), "solid|height"); tipsViewModel.setSolid(transactionViewModel.getHash()); addSolidTransaction(transactionViewModel.getHash()); } @@ -422,7 +422,7 @@ private boolean quickSetSolid(final TransactionViewModel transactionViewModel) t } if(solid) { transactionViewModel.updateSolid(true); - transactionViewModel.updateHeights(tangle, initialSnapshot); + transactionViewModel.updateHeights(tangle, snapshotProvider.getInitialSnapshot()); return true; } } @@ -440,7 +440,7 @@ private boolean checkApproovee(TransactionViewModel approovee) throws Exception transactionRequester.requestTransaction(approovee.getHash(), false); return false; } - if(initialSnapshot.hasSolidEntryPoint(approovee.getHash())) { + if(snapshotProvider.getInitialSnapshot().hasSolidEntryPoint(approovee.getHash())) { return true; } return approovee.isSolid(); diff --git a/src/main/java/com/iota/iri/network/Node.java b/src/main/java/com/iota/iri/network/Node.java index 805fc51eb8..6fef059f68 100644 --- a/src/main/java/com/iota/iri/network/Node.java +++ b/src/main/java/com/iota/iri/network/Node.java @@ -1,15 +1,15 @@ package com.iota.iri.network; -import com.iota.iri.MilestoneTracker; import com.iota.iri.TransactionValidator; import com.iota.iri.conf.NodeConfig; import com.iota.iri.controllers.TipsViewModel; import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.crypto.SpongeFactory; -import com.iota.iri.service.snapshot.Snapshot; +import com.iota.iri.service.milestone.LatestMilestoneTracker; import com.iota.iri.model.Hash; import com.iota.iri.model.HashFactory; import com.iota.iri.model.TransactionHash; +import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.storage.Tangle; import com.iota.iri.zmq.MessageQ; import org.apache.commons.lang3.StringUtils; @@ -56,10 +56,10 @@ public class Node { private final ExecutorService executor = Executors.newFixedThreadPool(5); private final NodeConfig configuration; private final Tangle tangle; - private final Snapshot initialSnapshot; + private final SnapshotProvider snapshotProvider; private final TipsViewModel tipsViewModel; private final TransactionValidator transactionValidator; - private final MilestoneTracker milestoneTracker; + private final LatestMilestoneTracker latestMilestoneTracker; private final TransactionRequester transactionRequester; private final MessageQ messageQ; @@ -78,15 +78,15 @@ public class Node { public static final ConcurrentSkipListSet rejectedAddresses = new ConcurrentSkipListSet(); private DatagramSocket udpSocket; - public Node(final Tangle tangle, Snapshot initialSnapshot, final TransactionValidator transactionValidator, final TransactionRequester transactionRequester, final TipsViewModel tipsViewModel, final MilestoneTracker milestoneTracker, final MessageQ messageQ, final NodeConfig configuration + public Node(final Tangle tangle, SnapshotProvider snapshotProvider, final TransactionValidator transactionValidator, final TransactionRequester transactionRequester, final TipsViewModel tipsViewModel, final LatestMilestoneTracker latestMilestoneTracker, final MessageQ messageQ, final NodeConfig configuration ) { this.configuration = configuration; this.tangle = tangle; - this.initialSnapshot = initialSnapshot; + this.snapshotProvider = snapshotProvider ; this.transactionValidator = transactionValidator; this.transactionRequester = transactionRequester; this.tipsViewModel = tipsViewModel; - this.milestoneTracker = milestoneTracker; + this.latestMilestoneTracker = latestMilestoneTracker ; this.messageQ = messageQ; this.reqHashSize = configuration.getRequestHashSize(); int packetSize = configuration.getTransactionPacketSize(); @@ -366,7 +366,7 @@ public void processReceivedData(TransactionViewModel receivedTransactionViewMode //store new transaction try { - stored = receivedTransactionViewModel.store(tangle, initialSnapshot); + stored = receivedTransactionViewModel.store(tangle, snapshotProvider.getInitialSnapshot()); } catch (Exception e) { log.error("Error accessing persistence store.", e); neighbor.incInvalidTransactions(); @@ -378,7 +378,7 @@ public void processReceivedData(TransactionViewModel receivedTransactionViewMode try { transactionValidator.updateStatus(receivedTransactionViewModel); receivedTransactionViewModel.updateSender(neighbor.getAddress().toString()); - receivedTransactionViewModel.update(tangle, initialSnapshot, "arrivalTime|sender"); + receivedTransactionViewModel.update(tangle, snapshotProvider.getInitialSnapshot(), "arrivalTime|sender"); } catch (Exception e) { log.error("Error updating transactions.", e); } @@ -449,7 +449,7 @@ public void replyToRequest(Hash requestedHash, Neighbor neighbor) { } private Hash getRandomTipPointer() throws Exception { - Hash tip = rnd.nextDouble() < configuration.getpSendMilestone() ? milestoneTracker.latestMilestone : tipsViewModel.getRandomSolidTipHash(); + Hash tip = rnd.nextDouble() < configuration.getpSendMilestone() ? latestMilestoneTracker.getLatestMilestoneHash() : tipsViewModel.getRandomSolidTipHash(); return tip == null ? Hash.NULL_HASH : tip; } @@ -515,7 +515,7 @@ private Runnable spawnTipRequesterThread() { while (!shuttingDown.get()) { try { - final TransactionViewModel transactionViewModel = TransactionViewModel.fromHash(tangle, milestoneTracker.latestMilestone); + final TransactionViewModel transactionViewModel = TransactionViewModel.fromHash(tangle, latestMilestoneTracker.getLatestMilestoneHash()); System.arraycopy(transactionViewModel.getBytes(), 0, tipRequestingPacket.getData(), 0, TransactionViewModel.SIZE); System.arraycopy(transactionViewModel.getHash().bytes(), 0, tipRequestingPacket.getData(), TransactionViewModel.SIZE, reqHashSize); diff --git a/src/main/java/com/iota/iri/network/TransactionRequester.java b/src/main/java/com/iota/iri/network/TransactionRequester.java index 8ab5f4150b..e3d1db8b61 100644 --- a/src/main/java/com/iota/iri/network/TransactionRequester.java +++ b/src/main/java/com/iota/iri/network/TransactionRequester.java @@ -2,7 +2,7 @@ import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.model.Hash; -import com.iota.iri.service.snapshot.Snapshot; +import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.zmq.MessageQ; import com.iota.iri.storage.Tangle; import org.apache.commons.lang3.ArrayUtils; @@ -30,11 +30,11 @@ public class TransactionRequester { private final Object syncObj = new Object(); private final Tangle tangle; - private final Snapshot initialSnapshot; + private final SnapshotProvider snapshotProvider; - public TransactionRequester(Tangle tangle, Snapshot initialSnapshot, MessageQ messageQ) { + public TransactionRequester(Tangle tangle, SnapshotProvider snapshotProvider, MessageQ messageQ) { this.tangle = tangle; - this.initialSnapshot = initialSnapshot; + this.snapshotProvider = snapshotProvider; this.messageQ = messageQ; } @@ -65,7 +65,7 @@ public boolean clearTransactionRequest(Hash hash) { } public void requestTransaction(Hash hash, boolean milestone) throws Exception { - if (!initialSnapshot.hasSolidEntryPoint(hash) && !TransactionViewModel.exists(tangle, hash)) { + if (!snapshotProvider.getInitialSnapshot().hasSolidEntryPoint(hash) && !TransactionViewModel.exists(tangle, hash)) { synchronized (syncObj) { if(milestone) { transactionsToRequest.remove(hash); diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java index 50f90ae46f..fd555f1710 100644 --- a/src/main/java/com/iota/iri/service/API.java +++ b/src/main/java/com/iota/iri/service/API.java @@ -2,7 +2,6 @@ import com.iota.iri.*; import com.iota.iri.conf.APIConfig; -import com.iota.iri.conf.ConsensusConfig; import com.iota.iri.controllers.AddressViewModel; import com.iota.iri.controllers.BundleViewModel; import com.iota.iri.controllers.TagViewModel; @@ -16,6 +15,7 @@ import com.iota.iri.model.persistables.Transaction; import com.iota.iri.network.Neighbor; import com.iota.iri.service.dto.*; +import com.iota.iri.service.snapshot.Snapshot; import com.iota.iri.service.tipselection.TipSelector; import com.iota.iri.service.tipselection.impl.WalkValidatorImpl; import com.iota.iri.utils.Converter; @@ -63,14 +63,14 @@ /** *

- * The API makes it possible to interact with the node by requesting information or actions to be taken. + * The API makes it possible to interact with the node by requesting information or actions to be taken. * You can interact with it by passing a JSON object which at least contains a command. * Upon successful execution of the command, the API returns your requested information in an {@link AbstractResponse}. *

*

- * If the request is invalid, an {@link ErrorResponse} is returned. + * If the request is invalid, an {@link ErrorResponse} is returned. * This, for example, happens when the command does not exist or there is no command section at all. - * If there is an error in the given data during the execution of a command, an {@link ErrorResponse} is also sent. + * If there is an error in the given data during the execution of a command, an {@link ErrorResponse} is also sent. *

*

* If an Exception is thrown during the execution of a command, an {@link ExceptionResponse} is returned. @@ -81,13 +81,12 @@ public class API { public static final String REFERENCE_TRANSACTION_NOT_FOUND = "reference transaction not found"; public static final String REFERENCE_TRANSACTION_TOO_OLD = "reference transaction is too old"; - + public static final String INVALID_SUBTANGLE = "This operation cannot be executed: " + "The subtangle has not been updated yet."; - + private static final Logger log = LoggerFactory.getLogger(API.class); private final IXI ixi; - private final int milestoneStartIndex; private Undertow server; @@ -122,9 +121,9 @@ public class API { /** * Starts loading the IOTA API, parameters do not have to be initialized. - * + * * @param instance The data source we interact with during any API call. - * @param ixi If a command is not in the standard API, + * @param ixi If a command is not in the standard API, * we try to process it as a Nashorn JavaScript module through {@link IXI} */ public API(Iota instance, IXI ixi) { @@ -136,7 +135,6 @@ public API(Iota instance, IXI ixi) { maxGetTrytes = configuration.getMaxGetTrytes(); maxBodyLength = configuration.getMaxBodyLength(); testNet = configuration.isTestnet(); - milestoneStartIndex = ((ConsensusConfig) configuration).getMilestoneStartIndex(); previousEpochsSpentAddresses = new ConcurrentHashMap<>(); @@ -153,12 +151,12 @@ public API(Iota instance, IXI ixi) { * If reading from the previous epoch fails, a log is printed. The API will continue to initialize. * *

  • - * Get the {@link APIConfig} from the {@link Iota} instance, + * Get the {@link APIConfig} from the {@link Iota} instance, * and read {@link APIConfig#getPort()} and {@link APIConfig#getApiHost()} *
  • *
  • - * Builds a secure {@link Undertow} server with the port and host. - * If {@link APIConfig#getRemoteAuth()} is defined, remote authentication is blocked for anyone except + * Builds a secure {@link Undertow} server with the port and host. + * If {@link APIConfig#getRemoteAuth()} is defined, remote authentication is blocked for anyone except * those defined in {@link APIConfig#getRemoteAuth()} or localhost. * This is done with {@link BasicAuthenticationMechanism} in a {@link AuthenticationMode#PRO_ACTIVE} mode. * By default, this authentication is disabled. @@ -167,8 +165,8 @@ public API(Iota instance, IXI ixi) { * Starts the server, opening it for HTTP API requests *
  • * - * - * @throws IOException If we are not on the testnet, and the previousEpochsSpentAddresses files cannot be found. + * + * @throws IOException If we are not on the testnet, and the previousEpochsSpentAddresses files cannot be found. * Currently this exception is caught in {@link #readPreviousEpochsSpentAddresses(boolean)} */ public void init() throws IOException { @@ -211,9 +209,9 @@ public void handleRequest(final HttpServerExchange exchange) throws Exception { /** * Read the spend addresses from the previous epoch. Used in {@link #wasAddressSpentFrom(Hash)}. * If this fails, a log is printed. The API will continue to initialize. - * + * * @param isTestnet If this node is running on the testnet. If this is true, nothing is loaded. - * @throws IOException If we are not on the testnet and previousEpochsSpentAddresses files cannot be found. + * @throws IOException If we are not on the testnet and previousEpochsSpentAddresses files cannot be found. * Currently this exception is caught in {@link #readPreviousEpochsSpentAddresses(boolean)} */ private void readPreviousEpochsSpentAddresses(boolean isTestnet) throws IOException { @@ -225,7 +223,7 @@ private void readPreviousEpochsSpentAddresses(boolean isTestnet) throws IOExcept .configuration .getPreviousEpochSpentAddressesFiles() .split(" "); - + for (String previousEpochsSpentAddressesFile : previousEpochsSpentAddressesFiles) { InputStream in = Snapshot.class.getResourceAsStream(previousEpochsSpentAddressesFile); try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { @@ -238,9 +236,9 @@ private void readPreviousEpochsSpentAddresses(boolean isTestnet) throws IOExcept } } } - + /** - * Sends the API response back as JSON to the requester. + * Sends the API response back as JSON to the requester. * Status code of the HTTP request is also set according to the type of response. *
      *
    • {@link ErrorResponse}: 400
    • @@ -248,12 +246,12 @@ private void readPreviousEpochsSpentAddresses(boolean isTestnet) throws IOExcept *
    • {@link ExceptionResponse}: 500
    • *
    • Default: 200
    • *
    - * + * * @param exchange Contains information about what the client sent to us - * @param res The response of the API. - * See {@link #processRequest(HttpServerExchange)} + * @param res The response of the API. + * See {@link #processRequest(HttpServerExchange)} * and {@link #process(String, InetSocketAddress)} for the different responses in each case. - * @param beginningTime The time when we received the request, in milliseconds. + * @param beginningTime The time when we received the request, in milliseconds. * This will be used to set the response duration in {@link AbstractResponse#setDuration(Integer)} * @throws IOException When connection to client has been lost - Currently being caught. */ @@ -263,13 +261,13 @@ private void sendResponse(HttpServerExchange exchange, AbstractResponse res, lon if (res instanceof ErrorResponse) { // bad request or invalid parameters - exchange.setStatusCode(400); + exchange.setStatusCode(400); } else if (res instanceof AccessLimitedResponse) { // API method not allowed - exchange.setStatusCode(401); + exchange.setStatusCode(401); } else if (res instanceof ExceptionResponse) { // internal error - exchange.setStatusCode(500); + exchange.setStatusCode(500); } setupResponseHeaders(exchange); @@ -299,17 +297,17 @@ private void sendResponse(HttpServerExchange exchange, AbstractResponse res, lon /** *

    - * Processes an API HTTP request. + * Processes an API HTTP request. * No checks have been done until now, except that it is not an OPTIONS request. * We can be sure that we are in a thread that allows blocking. *

    *

    - * The request process duration is recorded. + * The request process duration is recorded. * During this the request gets verified. If it is incorrect, an {@link ErrorResponse} is made. * Otherwise it is processed in {@link #process(String, InetSocketAddress)}. * The result is sent back to the requester. *

    - * + * * @param exchange Contains the data the client sent to us * @throws IOException If the body of this HTTP request cannot be read */ @@ -332,7 +330,7 @@ private void processRequest(final HttpServerExchange exchange) throws IOExceptio } /** - * Handles an API request body. + * Handles an API request body. * Its returned {@link AbstractResponse} is created using the following logic *
      *
    • @@ -351,19 +349,19 @@ private void processRequest(final HttpServerExchange exchange) throws IOExceptio * {@link ExceptionResponse} if we encountered an unexpected exception during command processing. *
    • *
    • - * {@link AbstractResponse} when the command is successfully processed. + * {@link AbstractResponse} when the command is successfully processed. * The response class depends on the command executed. *
    • *
    - * + * * @param requestString The JSON encoded data of the request. * This String is attempted to be converted into a {@code Map}. * @param sourceAddress The address from the sender of this API request. - * @return The result of this request. - * @throws UnsupportedEncodingException If the requestString cannot be parsed into a Map. + * @return The result of this request. + * @throws UnsupportedEncodingException If the requestString cannot be parsed into a Map. Currently caught and turned into a {@link ExceptionResponse}. */ - private AbstractResponse process(final String requestString, InetSocketAddress sourceAddress) + private AbstractResponse process(final String requestString, InetSocketAddress sourceAddress) throws UnsupportedEncodingException { try { @@ -379,7 +377,7 @@ private AbstractResponse process(final String requestString, InetSocketAddress s return ErrorResponse.create("COMMAND parameter has not been specified in the request."); } - // Is this command allowed to be run from this request address? + // Is this command allowed to be run from this request address? // We check the remote limit API configuration. if (instance.configuration.getRemoteLimitApi().contains(command) && !sourceAddress.getAddress().isLoopbackAddress()) { @@ -527,7 +525,7 @@ private AbstractResponse process(final String requestString, InetSocketAddress s /** * Check if a list of addresses was ever spent from, in the current epoch, or in previous epochs. * If an address has a pending transaction, it is also marked as spend. - * + * * @param addresses List of addresses to check if they were ever spent from. **/ private AbstractResponse wereAddressesSpentFromStatement(List addresses) throws Exception { @@ -547,7 +545,7 @@ private AbstractResponse wereAddressesSpentFromStatement(List addresses) /** * Checks if the address was ever spent from, in the current epoch, or in previous epochs. * If an address has a pending transaction, it is also marked as spent. - * + * * @param address The address to check if it was ever spent from. * @return true if it was spent from, otherwise false * @throws Exception When a model could not be loaded. @@ -556,7 +554,7 @@ private boolean wasAddressSpentFrom(Hash address) throws Exception { if (previousEpochsSpentAddresses.containsKey(address)) { return true; } - + Set hashes = AddressViewModel.load(instance.tangle, address).getHashes(); for (Hash hash : hashes) { final TransactionViewModel tx = TransactionViewModel.fromHash(instance.tangle, hash); @@ -566,7 +564,7 @@ private boolean wasAddressSpentFrom(Hash address) throws Exception { if (tx.snapshotIndex() != 0) { return true; } - + // Transaction is pending Hash tail = findTail(hash); if (tail != null && BundleValidator.validate(instance.tangle, instance.snapshotProvider.getInitialSnapshot(), tail).size() != 0) { @@ -574,7 +572,7 @@ private boolean wasAddressSpentFrom(Hash address) throws Exception { } } } - + // No spending transaction found return false; } @@ -582,7 +580,7 @@ private boolean wasAddressSpentFrom(Hash address) throws Exception { /** * Walks back from the hash until a tail transaction has been found or transaction aprovee is not found. * A tail transaction is the first transaction in a bundle, thus with index = 0 - * + * * @param hash The transaction hash where we start the search from. If this is a tail, its hash is returned. * @return The transaction hash of the tail * @throws Exception When a model could not be loaded. @@ -592,7 +590,7 @@ private Hash findTail(Hash hash) throws Exception { final Hash bundleHash = tx.getBundleHash(); long index = tx.getCurrentIndex(); boolean foundApprovee = false; - + // As long as the index is bigger than 0 and we are still traversing the same bundle // If the hash we asked about is already a tail, this loop never starts while (index-- > 0 && tx.getBundleHash().equals(bundleHash)) { @@ -609,7 +607,7 @@ private Hash findTail(Hash hash) throws Exception { break; } } - + if (tx.getCurrentIndex() == 0) { return tx.getHash(); } @@ -618,7 +616,7 @@ private Hash findTail(Hash hash) throws Exception { /** - * + * * Checks the consistency of the transactions. * Marks state as false on the following checks: *
      @@ -626,9 +624,9 @@ private Hash findTail(Hash hash) throws Exception { *
    • Invalid bundle
    • *
    • Tails of tails are invalid
    • *
    - * + * * If a transaction does not exist, or it is not a tail, an {@link ErrorResponse} is returned. - * + * * @param transactionsList Transactions you want to check the consistency for * @return {@link CheckConsistency} **/ @@ -661,10 +659,10 @@ private AbstractResponse checkConsistencyStatement(List transactionsList // Transactions are valid, lets check ledger consistency if (state) { - instance.milestoneTracker.latestSnapshot.rwlock.readLock().lock(); + instance.snapshotProvider.getLatestSnapshot().lockRead(); try { - WalkValidatorImpl walkValidator = new WalkValidatorImpl(instance.tangle, instance.snapshotProvider, instance.ledgerValidator, - instance.milestoneTracker, instance.configuration); + WalkValidatorImpl walkValidator = new WalkValidatorImpl(instance.tangle, instance.snapshotProvider, instance.ledgerService, + instance.configuration); for (Hash transaction : transactions) { if (!walkValidator.isValid(transaction)) { state = false; @@ -673,7 +671,7 @@ private AbstractResponse checkConsistencyStatement(List transactionsList } } } finally { - instance.milestoneTracker.latestSnapshot.rwlock.readLock().unlock(); + instance.snapshotProvider.getLatestSnapshot().unlockRead(); } } @@ -683,13 +681,13 @@ private AbstractResponse checkConsistencyStatement(List transactionsList /** * Compares the last received confirmed milestone with the last global snapshot milestone. * If these are equal, it means the tangle is empty and therefore invalid. - * + * * @return false if we received at least a solid milestone, otherwise true */ public boolean invalidSubtangleStatus() { - return (instance.milestoneTracker.latestSolidSubtangleMilestoneIndex == milestoneStartIndex); + return (instance.snapshotProvider.getLatestSnapshot().getIndex() == instance.snapshotProvider.getInitialSnapshot().getIndex()); } - + /** * Returns the set of neighbors you are connected with, as well as their activity statistics (or counters). * The activity counters are reset after restarting IRI. @@ -699,11 +697,11 @@ public boolean invalidSubtangleStatus() { private AbstractResponse getNeighborsStatement() { return GetNeighborsResponse.create(instance.node.getNeighbors()); } - + /** * Temporarily add a list of neighbors to your node. * The added neighbors will not be available after restart. - * Add the neighbors to your config file + * Add the neighbors to your config file * or supply them in the -n command line option if you want to add them permanently. * * The URI (Unique Resource Identification) for adding neighbors is: @@ -779,19 +777,19 @@ private synchronized AbstractResponse getTrytesStatement(List hashes) th return GetTrytesResponse.create(elements); } - + private static int counterGetTxToApprove = 0; - + /** * Can be 0 or more, and is set to 0 every 100 requests. * Each increase indicates another 2 tips send. - * + * * @return The current amount of times this node has returned transactions to approve */ private static int getCounterGetTxToApprove() { return counterGetTxToApprove; } - + /** * Increases the amount of tips send for transactions to approve by one */ @@ -800,19 +798,19 @@ private static void incCounterGetTxToApprove() { } private static long ellapsedTime_getTxToApprove = 0L; - + /** * Can be 0 or more, and is set to 0 every 100 requests. - * + * * @return The current amount of time spent on sending transactions to approve in milliseconds */ private static long getEllapsedTimeGetTxToApprove() { return ellapsedTime_getTxToApprove; } - + /** * Increases the current amount of time spent on sending transactions to approve - * + * * @param ellapsedTime the time to add, in milliseconds */ private static void incEllapsedTimeGetTxToApprove(long ellapsedTime) { @@ -850,12 +848,12 @@ private synchronized AbstractResponse getTransactionsToApproveStatement(int dept /** * Gets tips which can be used by new transactions to approve. * If debug is enabled, statistics on tip selection will be gathered. - * + * * @param depth The milestone depth for finding the transactions to approve. * @param reference An optional transaction hash to be referenced by tips. * @return The tips which can be approved. * @throws Exception if the subtangle is out of date or if we fail to retrieve transaction tips. - * @see TipSelector + * @see TipSelector */ List getTransactionToApproveTips(int depth, Optional reference) throws Exception { if (invalidSubtangleStatus()) { @@ -872,9 +870,9 @@ List getTransactionToApproveTips(int depth, Optional reference) thro /** *

    - * Handles statistics on tip selection. + * Handles statistics on tip selection. * Increases the tip selection by one use. - *

    + *

    *

    * If the {@link #getCounterGetTxToApprove()} is a power of 100, a log is send and counters are reset. *

    @@ -883,9 +881,9 @@ private void gatherStatisticsOnTipSelection() { API.incCounterGetTxToApprove(); if ((getCounterGetTxToApprove() % 100) == 0) { String sb = "Last 100 getTxToApprove consumed " - + API.getEllapsedTimeGetTxToApprove() / 1000000000L + + API.getEllapsedTimeGetTxToApprove() / 1000000000L + " seconds processing time."; - + log.debug(sb); counterGetTxToApprove = 0; ellapsedTime_getTxToApprove = 0L; @@ -922,7 +920,7 @@ public void storeTransactionsStatement(List trytes) throws Exception { instance.transactionValidator.getMinWeightMagnitude()); elements.add(transactionViewModel); } - + for (final TransactionViewModel transactionViewModel : elements) { //store transactions if(transactionViewModel.store(instance.tangle, instance.snapshotProvider.getInitialSnapshot())) { @@ -933,7 +931,7 @@ public void storeTransactionsStatement(List trytes) throws Exception { } } } - + /** * Interrupts and completely aborts the attachToTangle process. * @@ -951,19 +949,20 @@ private AbstractResponse interruptAttachingToTangleStatement(){ **/ private AbstractResponse getNodeInfoStatement(){ String name = instance.configuration.isTestnet() ? IRI.TESTNET_NAME : IRI.MAINNET_NAME; - return GetNodeInfoResponse.create(name, IRI.VERSION, + return GetNodeInfoResponse.create(name, IRI.VERSION, Runtime.getRuntime().availableProcessors(), - Runtime.getRuntime().freeMemory(), - System.getProperty("java.version"), + Runtime.getRuntime().freeMemory(), + System.getProperty("java.version"), Runtime.getRuntime().maxMemory(), - Runtime.getRuntime().totalMemory(), - instance.milestoneTracker.latestMilestone, instance.milestoneTracker.latestMilestoneIndex, - instance.milestoneTracker.latestSolidSubtangleMilestone, - instance.milestoneTracker.latestSolidSubtangleMilestoneIndex, - instance.milestoneTracker.milestoneStartIndex, - instance.node.howManyNeighbors(), + Runtime.getRuntime().totalMemory(), + instance.latestMilestoneTracker.getLatestMilestoneHash(), + instance.latestMilestoneTracker.getLatestMilestoneIndex(), + instance.snapshotProvider.getLatestSnapshot().getHash(), + instance.snapshotProvider.getLatestSnapshot().getIndex(), + instance.snapshotProvider.getInitialSnapshot().getIndex(), + instance.node.howManyNeighbors(), instance.node.queuedTransactionsSize(), - System.currentTimeMillis(), + System.currentTimeMillis(), instance.tipsViewModel.size(), instance.transactionRequester.numberOfTransactionsToRequest(), features, @@ -988,13 +987,13 @@ private AbstractResponse getNodeInfoStatement(){ * @throws Exception When a transaction cannot be loaded from hash **/ private AbstractResponse getInclusionStatesStatement( - final List transactions, + final List transactions, final List tips) throws Exception { - + final List trans = transactions.stream() .map(HashFactory.TRANSACTION::create) .collect(Collectors.toList()); - + final List tps = tips.stream(). map(HashFactory.TRANSACTION::create) .collect(Collectors.toList()); @@ -1011,20 +1010,20 @@ private AbstractResponse getInclusionStatesStatement( } } } - + // Finds the lowest tips index, or 0 int minTipsIndex = tipsIndex.stream().reduce((a,b) -> a < b ? a : b).orElse(0); - - // If the lowest tips index (minTipsIndex) is 0 (or lower), + + // If the lowest tips index (minTipsIndex) is 0 (or lower), // we can't check transactions against snapshots because there were no tips, // or tips have not been confirmed by a snapshot yet if(minTipsIndex > 0) { // Finds the highest tips index, or 0 int maxTipsIndex = tipsIndex.stream().reduce((a,b) -> a > b ? a : b).orElse(0); int count = 0; - + // Checks transactions with indexes of tips, and sets inclusionStates byte to 1 or -1 accordingly - // Sets to -1 if the transaction is only known by hash, + // Sets to -1 if the transaction is only known by hash, // or has no index, or index is above the max tip index (not included). // Sets to 1 if the transaction index is below the max index of tips (included). @@ -1044,7 +1043,7 @@ private AbstractResponse getInclusionStatesStatement( Set analyzedTips = new HashSet<>(); Map sameIndexTransactionCount = new HashMap<>(); Map> sameIndexTips = new HashMap<>(); - + // Sorts all tips per snapshot index. Stops if a tip is not in our database, or just as a hash. for (final Hash tip : tps) { TransactionViewModel transactionViewModel = TransactionViewModel.fromHash(instance.tangle, tip); @@ -1055,7 +1054,7 @@ private AbstractResponse getInclusionStatesStatement( sameIndexTips.putIfAbsent(snapshotIndex, new LinkedList<>()); sameIndexTips.get(snapshotIndex).add(tip); } - + // Loop over all transactions without a state, and counts the amount per snapshot index for(int i = 0; i < inclusionStates.length; i++) { if(inclusionStates[i] == 0) { @@ -1065,18 +1064,18 @@ private AbstractResponse getInclusionStatesStatement( sameIndexTransactionCount.put(snapshotIndex, sameIndexTransactionCount.get(snapshotIndex) + 1); } } - + // Loop over all snapshot indexes of transactions that were not confirmed. // If we encounter an invalid tangle, stop this function completely. for(Integer index : sameIndexTransactionCount.keySet()) { // Get the tips from the snapshot indexes we are missing Queue sameIndexTip = sameIndexTips.get(index); - + // We have tips on the same level as transactions, do a manual search. if (sameIndexTip != null && !exhaustiveSearchWithinIndex( - sameIndexTip, analyzedTips, trans, + sameIndexTip, analyzedTips, trans, inclusionStates, sameIndexTransactionCount.get(index), index)) { - + return ErrorResponse.create(INVALID_SUBTANGLE); } } @@ -1085,47 +1084,47 @@ private AbstractResponse getInclusionStatesStatement( // If a state is 0 by now, we know nothing so assume not included inclusionStatesBoolean[i] = inclusionStates[i] == 1; } - + { return GetInclusionStatesResponse.create(inclusionStatesBoolean); } } - + /** * Traverses down the tips until all transactions we wish to validate have been found or transaction data is missing. - * + * * @param nonAnalyzedTransactions Tips we will analyze. - * @param analyzedTips The hashes of tips we have analyzed. + * @param analyzedTips The hashes of tips we have analyzed. * Hashes specified here won't be analyzed again. - * @param transactions All transactions we are validating. - * @param inclusionStates The state of each transaction. + * @param transactions All transactions we are validating. + * @param inclusionStates The state of each transaction. * 1 means confirmed, -1 means unconfirmed, 0 is unknown confirmation. * Should be of equal length as transactions. - * @param count The amount of transactions on the same index level as nonAnalyzedTransactions. + * @param count The amount of transactions on the same index level as nonAnalyzedTransactions. * @param index The snapshot index of the tips in nonAnalyzedTransactions. - * @return true if all transactions are directly or indirectly references by - * nonAnalyzedTransactions. + * @return true if all transactions are directly or indirectly references by + * nonAnalyzedTransactions. * If at some point we are missing transaction data false is returned immediately. * @throws Exception If a {@link TransactionViewModel} cannot be loaded. */ private boolean exhaustiveSearchWithinIndex( - Queue nonAnalyzedTransactions, - Set analyzedTips, - List transactions, + Queue nonAnalyzedTransactions, + Set analyzedTips, + List transactions, byte[] inclusionStates, int count, int index) throws Exception { - + Hash pointer; MAIN_LOOP: // While we have nonAnalyzedTransactions in the Queue while ((pointer = nonAnalyzedTransactions.poll()) != null) { // Only analyze tips we haven't analyzed yet if (analyzedTips.add(pointer)) { - + // Check if the transactions have indeed this index. Otherwise ignore. // Starts off with the tips in nonAnalyzedTransactions, but transaction trunk & branch gets added. final TransactionViewModel transactionViewModel = TransactionViewModel.fromHash(instance.tangle, pointer); if (transactionViewModel.snapshotIndex() == index) { - // Do we have the complete transaction? + // Do we have the complete transaction? if (transactionViewModel.getType() == TransactionViewModel.PREFILLED_SLOT) { // Incomplete transaction data, stop search. return false; @@ -1133,17 +1132,17 @@ private boolean exhaustiveSearchWithinIndex( // check all transactions we wish to verify confirmation for for (int i = 0; i < inclusionStates.length; i++) { if (inclusionStates[i] < 1 && pointer.equals(transactions.get(i))) { - // A tip, or its branch/trunk points to this transaction. + // A tip, or its branch/trunk points to this transaction. // That means this transaction is confirmed by this tip. inclusionStates[i] = 1; - + // Only stop search when we have found all transactions we were looking for if (--count <= 0) { break MAIN_LOOP; } } } - + // Add trunk and branch to the queue for the transaction confirmation check nonAnalyzedTransactions.offer(transactionViewModel.getTrunkTransactionHash()); nonAnalyzedTransactions.offer(transactionViewModel.getBranchTransactionHash()); @@ -1160,14 +1159,14 @@ private boolean exhaustiveSearchWithinIndex( * All input values are lists, for which a list of return values (transaction hashes), in the same order, is returned for all individual elements. * The input fields can either be bundles, addresses, tags or approvees. *

    - * + * * Using multiple of these input fields returns the intersection of the values. * Returns an {@link com.iota.iri.service.dto.ErrorResponse} if more than maxFindTxs was found. * * @param request The map with input fields * Must contain at least one of 'bundles', 'addresses', 'tags' or 'approvees'. * @return {@link com.iota.iri.service.dto.FindTransactionsResponse}. - * @throws Exception If a model cannot be loaded, no valid input fields were supplied + * @throws Exception If a model cannot be loaded, no valid input fields were supplied * or the total transactions to find exceeds {@link APIConfig#getMaxFindTransactions()}. **/ private synchronized AbstractResponse findTransactionsStatement(final Map request) throws Exception { @@ -1264,7 +1263,7 @@ private synchronized AbstractResponse findTransactionsStatement(final Maptag is a {@link Hash#NULL_HASH}. @@ -1281,23 +1280,23 @@ private String padTag(String tag) throws ValidationException { /** * Runs {@link #getParameterAsList(Map, String, int)} and transforms it into a {@link Set}. - * + * * @param request All request parameters. * @param paramName The name of the parameter we want to turn into a list of Strings. * @param size the length each String must have. * @return the list of valid Tryte Strings. - * @throws ValidationException If the requested parameter does not exist or + * @throws ValidationException If the requested parameter does not exist or * the string is not exactly trytes of size length or * the amount of Strings in the list exceeds {@link APIConfig#getMaxRequestsList} */ private Set getParameterAsSet( - Map request, + Map request, String paramName, int size) throws ValidationException { HashSet result = getParameterAsList(request,paramName,size) .stream() .collect(Collectors.toCollection(HashSet::new)); - + if (result.contains(Hash.NULL_HASH.toString())) { throw new ValidationException("Invalid " + paramName + " input"); } @@ -1308,7 +1307,7 @@ private Set getParameterAsSet( * Broadcast a list of transactions to all neighbors. * The trytes to be used for this call should be valid, attached transaction trytes. * These trytes are returned by attachToTangle, or by doing proof of work somewhere else. - * + * * @param trytes the list of transaction trytes to broadcast **/ public void broadcastTransactionsStatement(List trytes) { @@ -1319,7 +1318,7 @@ public void broadcastTransactionsStatement(List trytes) { Converter.trits(tryte, txTrits, 0); final TransactionViewModel transactionViewModel = instance.transactionValidator.validateTrits( txTrits, instance.transactionValidator.getMinWeightMagnitude()); - + elements.add(transactionViewModel); } for (final TransactionViewModel transactionViewModel : elements) { @@ -1332,10 +1331,10 @@ public void broadcastTransactionsStatement(List trytes) { /** *

    - * Calculates the confirmed balance, as viewed by the specified tips. - * If you do not specify the referencing tips, + * Calculates the confirmed balance, as viewed by the specified tips. + * If you do not specify the referencing tips, * the returned balance is based on the latest confirmed milestone. - * In addition to the balances, it also returns the referencing tips (or milestone), + * In addition to the balances, it also returns the referencing tips (or milestone), * as well as the index with which the confirmed balance was determined. * The balances are returned as a list in the same order as the addresses were provided as input. *

    @@ -1343,13 +1342,13 @@ public void broadcastTransactionsStatement(List trytes) { * * @param addresses The addresses where we will find the balance for. * @param tips The optional tips to find the balance through. - * @param threshold The confirmation threshold between 0 and 100(inclusive). + * @param threshold The confirmation threshold between 0 and 100(inclusive). * Should be set to 100 for getting balance by counting only confirmed transactions. * @return {@link com.iota.iri.service.dto.GetBalancesResponse} * @throws Exception When the database has encountered an error **/ - private AbstractResponse getBalancesStatement(List addresses, - List tips, + private AbstractResponse getBalancesStatement(List addresses, + List tips, int threshold) throws Exception { if (threshold <= 0 || threshold > 100) { @@ -1359,24 +1358,24 @@ private AbstractResponse getBalancesStatement(List addresses, final List addressList = addresses.stream() .map(address -> (HashFactory.ADDRESS.create(address))) .collect(Collectors.toCollection(LinkedList::new)); - + final List hashes; final Map balances = new HashMap<>(); - instance.milestoneTracker.latestSnapshot.rwlock.readLock().lock(); - final int index = instance.milestoneTracker.latestSnapshot.index(); - + instance.snapshotProvider.getLatestSnapshot().lockRead(); + final int index = instance.snapshotProvider.getLatestSnapshot().getIndex(); + if (tips == null || tips.size() == 0) { - hashes = Collections.singletonList(instance.milestoneTracker.latestSolidSubtangleMilestone); + hashes = Collections.singletonList(instance.snapshotProvider.getLatestSnapshot().getHash()); } else { hashes = tips.stream() .map(tip -> (HashFactory.TRANSACTION.create(tip))) .collect(Collectors.toCollection(LinkedList::new)); } - + try { // Get the balance for each address at the last snapshot for (final Hash address : addressList) { - Long value = instance.milestoneTracker.latestSnapshot.getBalance(address); + Long value = instance.snapshotProvider.getLatestSnapshot().getBalance(address); if (value == null) { value = 0L; } @@ -1392,15 +1391,15 @@ private AbstractResponse getBalancesStatement(List addresses, if (!TransactionViewModel.exists(instance.tangle, tip)) { return ErrorResponse.create("Tip not found: " + tip.toString()); } - if (!instance.ledgerValidator.updateDiff(visitedHashes, diff, tip)) { + if (!instance.ledgerService.isBalanceDiffConsistent(visitedHashes, diff, tip)) { return ErrorResponse.create("Tips are not consistent"); } } - + // Update the found balance according to 'diffs' balance changes diff.forEach((key, value) -> balances.computeIfPresent(key, (hash, aLong) -> value + aLong)); } finally { - instance.milestoneTracker.latestSnapshot.rwlock.readLock().unlock(); + instance.snapshotProvider.getLatestSnapshot().unlockRead(); } final List elements = addressList.stream() @@ -1413,18 +1412,18 @@ private AbstractResponse getBalancesStatement(List addresses, } private static int counter_PoW = 0; - + /** * Can be 0 or more, and is set to 0 every 100 requests. * Each increase indicates another 2 tips sent. - * - * @return The current amount of times this node has done proof of work. + * + * @return The current amount of times this node has done proof of work. * Doesn't distinguish between remote and local proof of work. */ public static int getCounterPoW() { return counter_PoW; } - + /** * Increases the amount of times this node has done proof of work by one. */ @@ -1433,20 +1432,20 @@ public static void incCounterPoW() { } private static long ellapsedTime_PoW = 0L; - + /** * Can be 0 or more, and is set to 0 every 100 requests. - * + * * @return The current amount of time spent on doing proof of work in milliseconds. - * Doesn't distinguish between remote and local proof of work. + * Doesn't distinguish between remote and local proof of work. */ public static long getEllapsedTimePoW() { return ellapsedTime_PoW; } - + /** * Increases the current amount of time spent on doing proof of work. - * + * * @param ellapsedTime the time to add, in milliseconds. */ public static void incEllapsedTimePoW(long ellapsedTime) { @@ -1457,11 +1456,11 @@ public static void incEllapsedTimePoW(long ellapsedTime) { *

    * Prepares the specified transactions (trytes) for attachment to the Tangle by doing Proof of Work. * You need to supply branchTransaction as well as trunkTransaction. - * These are the tips which you're going to validate and reference with this transaction. + * These are the tips which you're going to validate and reference with this transaction. * These are obtainable by the getTransactionsToApprove API call. *

    *

    - * The returned value is a different set of tryte values which you can input into + * The returned value is a different set of tryte values which you can input into * broadcastTransactions and storeTransactions. * The last 243 trytes of the return value consist of the following: *

      @@ -1474,12 +1473,12 @@ public static void incEllapsedTimePoW(long ellapsedTime) { * @param trunkTransaction A reference to an external transaction (tip) used as trunk. * The transaction with index 0 will have this tip in its trunk. * All other transactions reference the previous transaction in the bundle (Their index-1). - * + * * @param branchTransaction A reference to an external transaction (tip) used as branch. * Each Transaction in the bundle will have this tip as their branch, except the last. * The last one will have the branch in its trunk. - * @param minWeightMagnitude The amount of work we should do to confirm this transaction. - * Each 0-trit on the end of the transaction represents 1 magnitude. + * @param minWeightMagnitude The amount of work we should do to confirm this transaction. + * Each 0-trit on the end of the transaction represents 1 magnitude. * A 9-tryte represents 3 magnitudes, since a 9 is represented by 3 0-trits. * Transactions with a different minWeightMagnitude are compatible. * @param trytes the list of trytes to prepare for network attachment, by doing proof of work. @@ -1487,7 +1486,7 @@ public static void incEllapsedTimePoW(long ellapsedTime) { **/ public synchronized List attachToTangleStatement(Hash trunkTransaction, Hash branchTransaction, int minWeightMagnitude, List trytes) { - + final List transactionViewModels = new LinkedList<>(); Hash prevTransaction = null; @@ -1510,10 +1509,10 @@ public synchronized List attachToTangleStatement(Hash trunkTransaction, //attachment fields: tag and timestamps //tag - copy the obsolete tag to the attachment tag field only if tag isn't set. - if(IntStream.range(TransactionViewModel.TAG_TRINARY_OFFSET, + if(IntStream.range(TransactionViewModel.TAG_TRINARY_OFFSET, TransactionViewModel.TAG_TRINARY_OFFSET + TransactionViewModel.TAG_TRINARY_SIZE) .allMatch(idx -> transactionTrits[idx] == ((byte) 0))) { - + System.arraycopy(transactionTrits, TransactionViewModel.OBSOLETE_TAG_TRINARY_OFFSET, transactionTrits, TransactionViewModel.TAG_TRINARY_OFFSET, TransactionViewModel.TAG_TRINARY_SIZE); @@ -1543,8 +1542,8 @@ public synchronized List attachToTangleStatement(Hash trunkTransaction, API.incEllapsedTimePoW(System.nanoTime() - startTime); API.incCounterPoW(); if ( ( API.getCounterPoW() % 100) == 0 ) { - String sb = "Last 100 PoW consumed " - + API.getEllapsedTimePoW() / 1000000000L + String sb = "Last 100 PoW consumed " + + API.getEllapsedTimePoW() / 1000000000L + " seconds processing time."; log.info(sb); counter_PoW = 0; @@ -1559,10 +1558,10 @@ public synchronized List attachToTangleStatement(Hash trunkTransaction, } return elements; } - + /** - * Transforms an object parameter into an int. - * + * Transforms an object parameter into an int. + * * @param request A map of all request parameters * @param paramName The parameter we want to get as an int. * @return The integer value of this parameter @@ -1581,12 +1580,12 @@ private int getParameterAsInt(Map request, String paramName) thr /** * Transforms an object parameter into a String. - * + * * @param request A map of all request parameters * @param paramName The parameter we want to get as a String. * @param size The expected length of this String * @return The String value of this parameter - * @throws ValidationException If the requested parameter does not exist or + * @throws ValidationException If the requested parameter does not exist or * the string is not exactly trytes of size length */ private String getParameterAsStringAndValidate(Map request, String paramName, int size) throws ValidationException { @@ -1599,7 +1598,7 @@ private String getParameterAsStringAndValidate(Map request, Stri /** * Checks if a string is non 0 length, and contains exactly size amount of trytes. * Trytes are Strings containing only A-Z and the number 9. - * + * * @param paramName The name of the parameter this String came from. * @param size The amount of trytes it should contain. * @param result The String we validate. @@ -1624,15 +1623,15 @@ private void validateParamExists(Map request, String paramName) } /** - * Translates the parameter into a {@link List}. - * We then validate if the amount of elements does not exceed the maximum allowed. + * Translates the parameter into a {@link List}. + * We then validate if the amount of elements does not exceed the maximum allowed. * Afterwards we verify if each element is valid according to {@link #validateTrytes(String, int, String)}. - * + * * @param request All request parameters * @param paramName The name of the parameter we want to turn into a list of Strings * @param size the length each String must have * @return the list of valid Tryte Strings. - * @throws ValidationException If the requested parameter does not exist or + * @throws ValidationException If the requested parameter does not exist or * the string is not exactly trytes of size length or * the amount of Strings in the list exceeds {@link APIConfig#getMaxRequestsList} */ @@ -1657,7 +1656,7 @@ private List getParameterAsList(Map request, String para /** * Checks if a string is of a certain length, and contains exactly size amount of trytes. * Trytes are Strings containing only A-Z and the number 9. - * + * * @param trytes The String we validate. * @param length The amount of trytes it should contain. * @param zeroAllowed If set to '{@value #ZERO_LENGTH_ALLOWED}', an empty string is also valid. @@ -1677,7 +1676,7 @@ private boolean validTrytes(String trytes, int length, char zeroAllowed) { /** * Updates the {@link HttpServerExchange} {@link HeaderMap} with the proper response settings. - * @param exchange Contains information about what the client has send to us + * @param exchange Contains information about what the client has send to us */ private static void setupResponseHeaders(HttpServerExchange exchange) { final HeaderMap headerMap = exchange.getResponseHeaders(); @@ -1687,10 +1686,10 @@ private static void setupResponseHeaders(HttpServerExchange exchange) { /** * Sets up the {@link HttpHandler} to have correct security settings. - * Remote authentication is blocked for anyone except + * Remote authentication is blocked for anyone except * those defined in {@link APIConfig#getRemoteAuth()} or localhost. * This is done with {@link BasicAuthenticationMechanism} in a {@link AuthenticationMode#PRO_ACTIVE} mode. - * + * * @param toWrap the path handler used in creating the server. * @return The updated handler */ @@ -1707,9 +1706,9 @@ private HttpHandler addSecurity(HttpHandler toWrap) { HttpHandler handler = toWrap; handler = new AuthenticationCallHandler(handler); handler = new AuthenticationConstraintHandler(handler); - final List mechanisms = + final List mechanisms = Collections.singletonList(new BasicAuthenticationMechanism("Iota Realm")); - + handler = new AuthenticationMechanismsHandler(handler, mechanisms); handler = new SecurityInitialHandler(AuthenticationMode.PRO_ACTIVE, identityManager, handler); return handler; @@ -1742,7 +1741,7 @@ private synchronized AbstractResponse storeMessageStatement(String address, Stri final byte[] timestampTrits = new byte[TransactionViewModel.TIMESTAMP_TRINARY_SIZE]; Converter.copyTrits(System.currentTimeMillis(), timestampTrits, 0, timestampTrits.length); final String timestampTrytes = StringUtils.rightPad( - Converter.trytes(timestampTrits), + Converter.trytes(timestampTrits), timestampTrits.length / 3, '9'); final byte[] lastIndexTrits = new byte[TransactionViewModel.LAST_INDEX_TRINARY_SIZE]; @@ -1801,4 +1800,4 @@ private synchronized AbstractResponse storeMessageStatement(String address, Stri broadcastTransactionsStatement(powResult); return AbstractResponse.createEmptyResponse(); } -} \ No newline at end of file +} diff --git a/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java b/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java index 8b42447ef2..b3899ff5b2 100644 --- a/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java +++ b/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java @@ -17,8 +17,7 @@ import java.util.*; /** - * Represents the service that contains all the relevant business logic for modifying and calculating the ledger - * state.
      + * Creates a service instance that allows us to perform ledger state specific operations.
      *
      * This class is stateless and does not hold any domain specific models.
      */ @@ -26,40 +25,50 @@ public class LedgerServiceImpl implements LedgerService { /** * Holds the tangle object which acts as a database interface.
      */ - private final Tangle tangle; + private Tangle tangle; /** * Holds the snapshot provider which gives us access to the relevant snapshots.
      */ - private final SnapshotProvider snapshotProvider; + private SnapshotProvider snapshotProvider; /** * Holds a reference to the service instance containing the business logic of the snapshot package.
      */ - private final SnapshotService snapshotService; + private SnapshotService snapshotService; /** * Holds a reference to the service instance containing the business logic of the milestone package.
      */ - private final MilestoneService milestoneService; + private MilestoneService milestoneService; /** - * Creates a service instance that allows us to perform ledger state specific operations.
      + * Initializes the instance and registers its dependencies.
      *
      - * It simply stores the passed in dependencies in the internal properties.
      + * It simply stores the passed in values in their corresponding private properties.
      + *
      + * Note: Instead of handing over the dependencies in the constructor, we register them lazy. This allows us to have + * circular dependencies because the instantiation is separated from the dependency injection. To reduce the + * amount of code that is necessary to correctly instantiate this class, we return the instance itself which + * allows us to still instantiate, initialize and assign in one line - see Example:
      + *
      + * {@code LedgerService ledgerService = new LedgerServiceImpl().init(...);} * * @param tangle Tangle object which acts as a database interface * @param snapshotProvider snapshot provider which gives us access to the relevant snapshots - * @param snapshotService service instance of the snapshot package that allows us to rollback ledger states + * @param snapshotService service instance of the snapshot package that gives us access to packages' business logic * @param milestoneService contains the important business logic when dealing with milestones + * @return the initialized instance itself to allow chaining */ - public LedgerServiceImpl(Tangle tangle, SnapshotProvider snapshotProvider, SnapshotService snapshotService, + public LedgerServiceImpl init(Tangle tangle, SnapshotProvider snapshotProvider, SnapshotService snapshotService, MilestoneService milestoneService) { this.tangle = tangle; this.snapshotProvider = snapshotProvider; this.snapshotService = snapshotService; this.milestoneService = milestoneService; + + return this; } @Override diff --git a/src/main/java/com/iota/iri/service/milestone/impl/LatestMilestoneTrackerImpl.java b/src/main/java/com/iota/iri/service/milestone/impl/LatestMilestoneTrackerImpl.java index 605e81ed10..1eef78c02f 100644 --- a/src/main/java/com/iota/iri/service/milestone/impl/LatestMilestoneTrackerImpl.java +++ b/src/main/java/com/iota/iri/service/milestone/impl/LatestMilestoneTrackerImpl.java @@ -31,11 +31,12 @@ import static com.iota.iri.service.milestone.MilestoneValidity.VALID; /** - * This class implements the basic contract of the {@link LatestMilestoneTracker} that keeps track of the latest - * milestone by incorporating a background worker that periodically checks if new milestones have arrived.
      + * Creates a tracker that automatically detects new milestones by incorporating a background worker that periodically + * checks all transactions that are originating from the coordinator address and that exposes the found latest milestone + * via getters.
      *
      - * Knowing about the latest milestone and being able to compare it to the latest solid milestone allows us to determine - * if our node is "in sync".
      + * It can be used to determine the sync-status of the node by comparing these values against the latest solid + * milestone.
      */ public class LatestMilestoneTrackerImpl implements LatestMilestoneTracker { /** @@ -56,39 +57,39 @@ public class LatestMilestoneTrackerImpl implements LatestMilestoneTracker { /** * Holds the Tangle object which acts as a database interface.
      */ - private final Tangle tangle; + private Tangle tangle; /** * The snapshot provider which gives us access to the relevant snapshots that the node uses (for faster * bootstrapping).
      */ - private final SnapshotProvider snapshotProvider; + private SnapshotProvider snapshotProvider; /** * Service class containing the business logic of the milestone package.
      */ - private final MilestoneService milestoneService; + private MilestoneService milestoneService; /** * Holds a reference to the manager that takes care of solidifying milestones.
      */ - private final MilestoneSolidifier milestoneSolidifier; + private MilestoneSolidifier milestoneSolidifier; /** * Holds a reference to the ZeroMQ interface that allows us to emit messages for external recipients.
      */ - private final MessageQ messageQ; + private MessageQ messageQ; /** - * Holds a reference to the manager of the background worker.
      + * Holds the coordinator address which is used to filter possible milestone candidates.
      */ - private final SilentScheduledExecutorService executorService = new DedicatedScheduledExecutorService( - "Latest Milestone Tracker", log.delegate()); + private Hash coordinatorAddress; /** - * Holds the coordinator address which is used to filter possible milestone candidates.
      + * Holds a reference to the manager of the background worker.
      */ - private final Hash coordinatorAddress; + private final SilentScheduledExecutorService executorService = new DedicatedScheduledExecutorService( + "Latest Milestone Tracker", log.delegate()); /** * Holds the milestone index of the latest milestone that we have seen / processed.
      @@ -123,13 +124,17 @@ public class LatestMilestoneTrackerImpl implements LatestMilestoneTracker { private boolean initialized = false; /** - * Creates a tracker that automatically detects new milestones by incorporating a background worker that - * periodically checks all transactions that are originating from the coordinator address and that exposes the found - * latest milestone via getters.
      + * This method initializes the instance and registers its dependencies.
      *
      - * It can be used to determine the sync-status of the node by comparing these values against the latest solid - * milestone. It simply stores the passed in parameters in their corresponding properties and bootstraps the - * tracker with values for the latest milestone that can be found quickly.
      + * It simply stores the passed in values in their corresponding private properties and bootstraps the latest + * milestone with values for the latest milestone that can be found quickly.
      + *
      + * Note: Instead of handing over the dependencies in the constructor, we register them lazy. This allows us to have + * circular dependencies because the instantiation is separated from the dependency injection. To reduce the + * amount of code that is necessary to correctly instantiate this class, we return the instance itself which + * allows us to still instantiate, initialize and assign in one line - see Example:
      + *
      + * {@code LatestMilestoneTracker latestMilestoneTracker = new LatestMilestoneTrackerImpl().init(...);} * * @param tangle Tangle object which acts as a database interface * @param snapshotProvider manager for the snapshots that allows us to retrieve the relevant snapshots of this node @@ -137,8 +142,9 @@ public class LatestMilestoneTrackerImpl implements LatestMilestoneTracker { * @param milestoneSolidifier manager that takes care of solidifying milestones * @param messageQ ZeroMQ interface that allows us to emit messages for external recipients * @param config configuration object which allows us to determine the important config parameters of the node + * @return the initialized instance itself to allow chaining */ - public LatestMilestoneTrackerImpl(Tangle tangle, SnapshotProvider snapshotProvider, + public LatestMilestoneTrackerImpl init(Tangle tangle, SnapshotProvider snapshotProvider, MilestoneService milestoneService, MilestoneSolidifier milestoneSolidifier, MessageQ messageQ, IotaConfig config) { @@ -151,6 +157,8 @@ public LatestMilestoneTrackerImpl(Tangle tangle, SnapshotProvider snapshotProvid coordinatorAddress = HashFactory.ADDRESS.create(config.getCoordinator()); bootstrapLatestMilestoneValue(); + + return this; } /** diff --git a/src/main/java/com/iota/iri/service/milestone/impl/LatestSolidMilestoneTrackerImpl.java b/src/main/java/com/iota/iri/service/milestone/impl/LatestSolidMilestoneTrackerImpl.java index 651602da2b..346dcf71bc 100644 --- a/src/main/java/com/iota/iri/service/milestone/impl/LatestSolidMilestoneTrackerImpl.java +++ b/src/main/java/com/iota/iri/service/milestone/impl/LatestSolidMilestoneTrackerImpl.java @@ -37,28 +37,28 @@ public class LatestSolidMilestoneTrackerImpl implements LatestSolidMilestoneTrac /** * Holds the Tangle object which acts as a database interface.
      */ - private final Tangle tangle; + private Tangle tangle; /** * The snapshot provider which gives us access to the relevant snapshots that the node uses (for the ledger * state).
      */ - private final SnapshotProvider snapshotProvider; + private SnapshotProvider snapshotProvider; /** * Holds a reference to the manager that keeps track of the latest milestone.
      */ - private final LatestMilestoneTracker latestMilestoneTracker; + private LatestMilestoneTracker latestMilestoneTracker; /** * Holds a reference to the service that contains the logic for applying milestones to the ledger state.
      */ - private final LedgerService ledgerService; + private LedgerService ledgerService; /** * Holds a reference to the ZeroMQ interface that allows us to emit messages for external recipients.
      */ - private final MessageQ messageQ; + private MessageQ messageQ; /** * Holds a reference to the manager of the background worker.
      @@ -67,19 +67,25 @@ public class LatestSolidMilestoneTrackerImpl implements LatestSolidMilestoneTrac "Latest Solid Milestone Tracker", log.delegate()); /** - * Creates a manager that keeps track of the latest solid milestones and that triggers the application of these - * milestones and their corresponding balance changes to the latest {@link Snapshot} by incorporating a background - * worker that periodically checks for new solid milestones.
      + * This method initializes the instance and registers its dependencies.
      *
      - * We simply store the passed in dependencies in their corresponding properties.
      + * It simply stores the passed in values in their corresponding private properties.
      + *
      + * Note: Instead of handing over the dependencies in the constructor, we register them lazy. This allows us to have + * circular dependencies because the instantiation is separated from the dependency injection. To reduce the + * amount of code that is necessary to correctly instantiate this class, we return the instance itself which + * allows us to still instantiate, initialize and assign in one line - see Example:
      + *
      + * {@code latestSolidMilestoneTracker = new LatestSolidMilestoneTrackerImpl().init(...);} * * @param tangle Tangle object which acts as a database interface * @param snapshotProvider manager for the snapshots that allows us to retrieve the relevant snapshots of this node - * @param latestMilestoneTracker the manager that keeps track of the late st milestone * @param ledgerService the manager for + * @param latestMilestoneTracker the manager that keeps track of the latest milestone * @param messageQ ZeroMQ interface that allows us to emit messages for external recipients + * @return the initialized instance itself to allow chaining */ - public LatestSolidMilestoneTrackerImpl(Tangle tangle, SnapshotProvider snapshotProvider, + public LatestSolidMilestoneTrackerImpl init(Tangle tangle, SnapshotProvider snapshotProvider, LedgerService ledgerService, LatestMilestoneTracker latestMilestoneTracker, MessageQ messageQ) { this.tangle = tangle; @@ -87,6 +93,8 @@ public LatestSolidMilestoneTrackerImpl(Tangle tangle, SnapshotProvider snapshotP this.ledgerService = ledgerService; this.latestMilestoneTracker = latestMilestoneTracker; this.messageQ = messageQ; + + return this; } @Override diff --git a/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java b/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java index e08dc30bd7..9f236ac1b1 100644 --- a/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java +++ b/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java @@ -29,7 +29,7 @@ import static com.iota.iri.service.milestone.MilestoneValidity.VALID; /** - * Represents the service that contains all the relevant business logic for interacting with milestones.
      + * Creates a service instance that allows us to perform milestone specific operations.
      *
      * This class is stateless and does not hold any domain specific models.
      */ @@ -37,40 +37,50 @@ public class MilestoneServiceImpl implements MilestoneService { /** * Holds the tangle object which acts as a database interface.
      */ - private final Tangle tangle; + private Tangle tangle; /** * Holds the snapshot provider which gives us access to the relevant snapshots.
      */ - private final SnapshotProvider snapshotProvider; + private SnapshotProvider snapshotProvider; /** * Holds the ZeroMQ interface that allows us to emit messages for external recipients.
      */ - private final MessageQ messageQ; + private MessageQ messageQ; /** * Holds the config with important milestone specific settings.
      */ - private final ConsensusConfig config; + private ConsensusConfig config; /** - * Creates a service instance that allows us to interact with the milestones.
      + * This method initializes the instance and registers its dependencies.
      *
      - * It simply stores the passed in dependencies in the internal properties.
      + * It simply stores the passed in values in their corresponding private properties.
      + *
      + * Note: Instead of handing over the dependencies in the constructor, we register them lazy. This allows us to have + * circular dependencies because the instantiation is separated from the dependency injection. To reduce the + * amount of code that is necessary to correctly instantiate this class, we return the instance itself which + * allows us to still instantiate, initialize and assign in one line - see Example:
      + *
      + * {@code milestoneService = new MilestoneServiceImpl().init(...);} * * @param tangle Tangle object which acts as a database interface * @param snapshotProvider snapshot provider which gives us access to the relevant snapshots * @param messageQ ZeroMQ interface that allows us to emit messages for external recipients * @param config config with important milestone specific settings + * @return the initialized instance itself to allow chaining */ - public MilestoneServiceImpl(Tangle tangle, SnapshotProvider snapshotProvider, MessageQ messageQ, + public MilestoneServiceImpl init(Tangle tangle, SnapshotProvider snapshotProvider, MessageQ messageQ, ConsensusConfig config) { this.tangle = tangle; this.snapshotProvider = snapshotProvider; this.messageQ = messageQ; this.config = config; + + return this; } //region {PUBLIC METHODS] ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/main/java/com/iota/iri/service/milestone/impl/MilestoneSolidifierImpl.java b/src/main/java/com/iota/iri/service/milestone/impl/MilestoneSolidifierImpl.java index fef5b28fc8..377ae6b0d9 100644 --- a/src/main/java/com/iota/iri/service/milestone/impl/MilestoneSolidifierImpl.java +++ b/src/main/java/com/iota/iri/service/milestone/impl/MilestoneSolidifierImpl.java @@ -3,7 +3,7 @@ import com.iota.iri.TransactionValidator; import com.iota.iri.model.Hash; import com.iota.iri.service.milestone.MilestoneSolidifier; -import com.iota.iri.service.snapshot.Snapshot; +import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.utils.log.interval.IntervalLogger; import com.iota.iri.utils.thread.DedicatedScheduledExecutorService; import com.iota.iri.utils.thread.SilentScheduledExecutorService; @@ -48,14 +48,14 @@ public class MilestoneSolidifierImpl implements MilestoneSolidifier { private static final IntervalLogger log = new IntervalLogger(MilestoneSolidifier.class); /** - * Holds a reference to the initial Snapshot which allows us to check if milestones are still relevant.
      + * Holds the snapshot provider which gives us access to the relevant snapshots.
      */ - private final Snapshot initialSnapshot; + private SnapshotProvider snapshotProvider; /** * Holds a reference to the TransactionValidator which allows us to issue solidity checks.
      */ - private final TransactionValidator transactionValidator; + private TransactionValidator transactionValidator; /** * Holds a reference to the manager of the background worker.
      @@ -91,16 +91,26 @@ public class MilestoneSolidifierImpl implements MilestoneSolidifier { private Map.Entry youngestMilestoneInQueue = null; /** - * Constructor of the class.
      + * This method initializes the instance and registers its dependencies.
      *
      - * It simply stores the passed in parameters to be able to access them later on.
      + * It simply stores the passed in values in their corresponding private properties.
      + *
      + * Note: Instead of handing over the dependencies in the constructor, we register them lazy. This allows us to have + * circular dependencies because the instantiation is separated from the dependency injection. To reduce the + * amount of code that is necessary to correctly instantiate this class, we return the instance itself which + * allows us to still instantiate, initialize and assign in one line - see Example:
      + *
      + * {@code milestoneSolidifier = new MilestoneSolidifierImpl().init(...);} * - * @param initialSnapshot initial Snapshot instance that is used by the node + * @param snapshotProvider snapshot provider which gives us access to the relevant snapshots * @param transactionValidator TransactionValidator instance that is used by the node + * @return the initialized instance itself to allow chaining */ - public MilestoneSolidifierImpl(Snapshot initialSnapshot, TransactionValidator transactionValidator) { - this.initialSnapshot = initialSnapshot; + public MilestoneSolidifierImpl init(SnapshotProvider snapshotProvider, TransactionValidator transactionValidator) { + this.snapshotProvider = snapshotProvider; this.transactionValidator = transactionValidator; + + return this; } /** @@ -113,7 +123,7 @@ public MilestoneSolidifierImpl(Snapshot initialSnapshot, TransactionValidator tr @Override public void add(Hash milestoneHash, int milestoneIndex) { if (!unsolidMilestonesPool.containsKey(milestoneHash) && !newlyAddedMilestones.containsKey(milestoneHash) && - milestoneIndex > initialSnapshot.getIndex()) { + milestoneIndex > snapshotProvider.getInitialSnapshot().getIndex()) { newlyAddedMilestones.put(milestoneHash, milestoneIndex); } @@ -212,7 +222,7 @@ private void processSolidificationQueue() { Map.Entry currentEntry = iterator.next(); - if (currentEntry.getValue() <= initialSnapshot.getIndex() || isSolid(currentEntry)) { + if (currentEntry.getValue() <= snapshotProvider.getInitialSnapshot().getIndex() || isSolid(currentEntry)) { unsolidMilestonesPool.remove(currentEntry.getKey()); iterator.remove(); diff --git a/src/main/java/com/iota/iri/service/milestone/impl/SeenMilestonesRetrieverImpl.java b/src/main/java/com/iota/iri/service/milestone/impl/SeenMilestonesRetrieverImpl.java index 676bf7dc50..b48b26e5ac 100644 --- a/src/main/java/com/iota/iri/service/milestone/impl/SeenMilestonesRetrieverImpl.java +++ b/src/main/java/com/iota/iri/service/milestone/impl/SeenMilestonesRetrieverImpl.java @@ -15,11 +15,13 @@ import java.util.concurrent.TimeUnit; /** - * This class implements the basic contract of the {@link SeenMilestonesRetriever} by providing a manager that requests - * milestones that are "in range" for "immediate solidification".
      + * Creates a manager that proactively requests the missing "seen milestones" (defined in the local snapshot file).
      *
      - * This means the manager does not request all missing milestones at once, but focuses on the milestones that are - * directly following our latest solid milestone which will allow us to sync + * It simply stores the passed in dependencies in their corresponding properties and then makes a copy of the {@code + * seenMilestones} of the initial snapshot which will consequently be requested.
      + *
      + * Once the manager finishes to request all "seen milestones" it will automatically {@link #shutdown()} (when being + * {@link #start()}ed before).
      */ public class SeenMilestonesRetrieverImpl implements SeenMilestonesRetriever { /** @@ -41,18 +43,18 @@ public class SeenMilestonesRetrieverImpl implements SeenMilestonesRetriever { /** * Tangle object which acts as a database interface.
      */ - private final Tangle tangle; + private Tangle tangle; /** * The snapshot provider which gives us access to the relevant snapshots to calculate our range.
      */ - private final SnapshotProvider snapshotProvider; + private SnapshotProvider snapshotProvider; /** * Holds a reference to the {@link TransactionRequester} that allows us to issue requests for the missing * milestones.
      */ - private final TransactionRequester transactionRequester; + private TransactionRequester transactionRequester; /** * Holds a reference to the manager of the background worker.
      @@ -63,28 +65,36 @@ public class SeenMilestonesRetrieverImpl implements SeenMilestonesRetriever { /** * The list of seen milestones that need to be requested.
      */ - private final Map seenMilestones; + private Map seenMilestones; /** - * Creates a manager that proactively requests the missing "seen milestones" (defined in the local snapshot - * file).
      + * This method initializes the instance and registers its dependencies.
      *
      - * It simply stores the passed in dependencies in their corresponding properties and then makes a copy of the - * {@code seenMilestones} of the initial snapshot which will consequently be requested.
      + * It simply stores the passed in values in their corresponding private properties and creates a working copy of the + * seen milestones (which will get processed by the background worker).
      *
      - * Once the manager finishes to request all "seen milestones" it will automatically {@link #shutdown()} (when being - * {@link #start()}ed before).
      + * Note: Instead of handing over the dependencies in the constructor, we register them lazy. This allows us to have + * circular dependencies because the instantiation is separated from the dependency injection. To reduce the + * amount of code that is necessary to correctly instantiate this class, we return the instance itself which + * allows us to still instantiate, initialize and assign in one line - see Example:
      + *
      + * {@code seenMilestonesRetriever = new SeenMilestonesRetrieverImpl().init(...);} * * @param tangle Tangle object which acts as a database interface * @param snapshotProvider snapshot provider which gives us access to the relevant snapshots to calculate our range * @param transactionRequester allows us to issue requests for the missing milestones + * @return the initialized instance itself to allow chaining */ - public SeenMilestonesRetrieverImpl(Tangle tangle, SnapshotProvider snapshotProvider, TransactionRequester transactionRequester) { + public SeenMilestonesRetrieverImpl init(Tangle tangle, SnapshotProvider snapshotProvider, + TransactionRequester transactionRequester) { + this.tangle = tangle; this.snapshotProvider = snapshotProvider; this.transactionRequester = transactionRequester; seenMilestones = new ConcurrentHashMap<>(snapshotProvider.getInitialSnapshot().getSeenMilestones()); + + return this; } /** diff --git a/src/main/java/com/iota/iri/service/snapshot/impl/LocalSnapshotManagerImpl.java b/src/main/java/com/iota/iri/service/snapshot/impl/LocalSnapshotManagerImpl.java index 36e423038b..b90ec2981a 100644 --- a/src/main/java/com/iota/iri/service/snapshot/impl/LocalSnapshotManagerImpl.java +++ b/src/main/java/com/iota/iri/service/snapshot/impl/LocalSnapshotManagerImpl.java @@ -7,7 +7,6 @@ import com.iota.iri.service.snapshot.SnapshotException; import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.service.transactionpruning.TransactionPruner; -import com.iota.iri.storage.Tangle; import com.iota.iri.utils.thread.ThreadIdentifier; import com.iota.iri.utils.thread.ThreadUtils; @@ -15,7 +14,11 @@ import org.slf4j.LoggerFactory; /** - * Implements the basic contract of the {@link LocalSnapshotManager}. + * Creates a manager for the local snapshots, that takes care of automatically creating local snapshots when the defined + * intervals have passed.
      + *
      + * It incorporates a background worker that periodically checks if a new snapshot is due (see {@link + * #start(LatestMilestoneTracker)} and {@link #shutdown()}).
      */ public class LocalSnapshotManagerImpl implements LocalSnapshotManager { /** @@ -32,27 +35,22 @@ public class LocalSnapshotManagerImpl implements LocalSnapshotManager { /** * Data provider for the relevant {@link com.iota.iri.service.snapshot.Snapshot} instances. */ - private final SnapshotProvider snapshotProvider; + private SnapshotProvider snapshotProvider; /** * Service that contains the logic for generating local {@link com.iota.iri.service.snapshot.Snapshot}s. */ - private final SnapshotService snapshotService; + private SnapshotService snapshotService; /** * Manager for the pruning jobs that allows us to clean up old transactions. */ - private final TransactionPruner transactionPruner; - - /** - * Tangle object which acts as a database interface. - */ - private final Tangle tangle; + private TransactionPruner transactionPruner; /** * Configuration with important snapshot related parameters. */ - private final SnapshotConfig config; + private SnapshotConfig config; /** * Holds a reference to the {@link ThreadIdentifier} for the monitor thread. @@ -63,24 +61,32 @@ public class LocalSnapshotManagerImpl implements LocalSnapshotManager { private ThreadIdentifier monitorThreadIdentifier = new ThreadIdentifier("Local Snapshots Monitor"); /** - * Creates the {@link LocalSnapshotManager} that takes care of automatically creating local snapshots when the - * defined intervals have passed. + * This method initializes the instance and registers its dependencies.
      + *
      + * It simply stores the passed in values in their corresponding private properties.
      + *
      + * Note: Instead of handing over the dependencies in the constructor, we register them lazy. This allows us to have + * circular dependencies because the instantiation is separated from the dependency injection. To reduce the + * amount of code that is necessary to correctly instantiate this class, we return the instance itself which + * allows us to still instantiate, initialize and assign in one line - see Example:
      + *
      + * {@code localSnapshotManager = new LocalSnapshotManagerImpl().init(...);} * - * It simply stores the passed in parameters in their private properties. - * - * @param snapshotProvider data provider for the relevant {@link com.iota.iri.service.snapshot.Snapshot} instances + * @param snapshotProvider data provider for the snapshots that are relevant for the node + * @param snapshotService service instance of the snapshot package that gives us access to packages' business logic * @param transactionPruner manager for the pruning jobs that allows us to clean up old transactions - * @param tangle object which acts as a database interface - * @param config configuration with important snapshot related parameters + * @param config important snapshot related configuration parameters + * @return the initialized instance itself to allow chaining */ - public LocalSnapshotManagerImpl(SnapshotProvider snapshotProvider, SnapshotService snapshotService, - TransactionPruner transactionPruner, Tangle tangle, SnapshotConfig config) { + public LocalSnapshotManagerImpl init(SnapshotProvider snapshotProvider, SnapshotService snapshotService, + TransactionPruner transactionPruner, SnapshotConfig config) { this.snapshotProvider = snapshotProvider; this.snapshotService = snapshotService; this.transactionPruner = transactionPruner; - this.tangle = tangle; this.config = config; + + return this; } /** diff --git a/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotProviderImpl.java b/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotProviderImpl.java index d150763e8d..d2e5113993 100644 --- a/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotProviderImpl.java +++ b/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotProviderImpl.java @@ -20,7 +20,18 @@ import java.util.stream.Stream; /** - * Implements the basic contract of the {@link SnapshotProvider} interface. + * Creates a data provider for the two {@link Snapshot} instances that are relevant for the node.
      + *
      + * It provides access to the two relevant {@link Snapshot} instances:
      + *
        + *
      • + * the {@link #initialSnapshot} (the starting point of the ledger based on the last global or local Snapshot) + *
      • + *
      • + * the {@link #latestSnapshot} (the state of the ledger after applying all changes up till the latest confirmed + * milestone) + *
      • + *
      */ public class SnapshotProviderImpl implements SnapshotProvider { /** @@ -56,7 +67,7 @@ public class SnapshotProviderImpl implements SnapshotProvider { /** * Holds Snapshot related configuration parameters. */ - private final SnapshotConfig config; + private SnapshotConfig config; /** * Internal property for the value returned by {@link SnapshotProvider#getInitialSnapshot()}. @@ -69,21 +80,28 @@ public class SnapshotProviderImpl implements SnapshotProvider { private Snapshot latestSnapshot; /** - * Creates a data provider for the two {@link Snapshot} instances that are relevant for the node. - * - * It provides access to the two relevant {@link Snapshot} instances: - * - * - the initial {@link Snapshot} (the starting point of the ledger based on the last global or local Snapshot) - * - the latest {@link Snapshot} (the state of the ledger after applying all changes up till the latest - * confirmed milestone) + * This method initializes the instance and registers its dependencies.
      + *
      + * It simply stores the passed in values in their corresponding private properties and loads the snapshots.
      + *
      + * Note: Instead of handing over the dependencies in the constructor, we register them lazy. This allows us to have + * circular dependencies because the instantiation is separated from the dependency injection. To reduce the + * amount of code that is necessary to correctly instantiate this class, we return the instance itself which + * allows us to still instantiate, initialize and assign in one line - see Example:
      + *
      + * {@code snapshotProvider = new SnapshotProviderImpl().init(...);} * * @param config Snapshot related configuration parameters * @throws SnapshotException if anything goes wrong while trying to read the snapshots + * @return the initialized instance itself to allow chaining + * */ - public SnapshotProviderImpl(SnapshotConfig config) throws SnapshotException { + public SnapshotProviderImpl init(SnapshotConfig config) throws SnapshotException { this.config = config; loadSnapshots(); + + return this; } /** diff --git a/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotServiceImpl.java b/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotServiceImpl.java index 5a9ec7e5aa..c548da4417 100644 --- a/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotServiceImpl.java +++ b/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotServiceImpl.java @@ -32,7 +32,9 @@ import java.util.stream.Collectors; /** - * Implements the basic contract of the {@link SnapshotService}. + * Creates a service instance that allows us to access the business logic for {@link Snapshot}s.
      + *
      + * The service instance is stateless and can be shared by multiple other consumers.
      */ public class SnapshotServiceImpl implements SnapshotService { /** @@ -57,31 +59,41 @@ public class SnapshotServiceImpl implements SnapshotService { /** * Holds the tangle object which acts as a database interface.
      */ - private final Tangle tangle; + private Tangle tangle; /** * Holds the snapshot provider which gives us access to the relevant snapshots.
      */ - private final SnapshotProvider snapshotProvider; + private SnapshotProvider snapshotProvider; /** * Holds the config with important snapshot specific settings.
      */ - private final SnapshotConfig config; + private SnapshotConfig config; /** - * Creates a service instance that allows us to interact with the snapshots.
      + * This method initializes the instance and registers its dependencies.
      *
      - * It simply stores the passed in dependencies in the internal properties.
      + * It simply stores the passed in values in their corresponding private properties.
      + *
      + * Note: Instead of handing over the dependencies in the constructor, we register them lazy. This allows us to have + * circular dependencies because the instantiation is separated from the dependency injection. To reduce the + * amount of code that is necessary to correctly instantiate this class, we return the instance itself which + * allows us to still instantiate, initialize and assign in one line - see Example:
      + *
      + * {@code snapshotService = new SnapshotServiceImpl().init(...);} * * @param tangle Tangle object which acts as a database interface - * @param snapshotProvider data provider for the {@link Snapshot}s that are relevant for the node + * @param snapshotProvider data provider for the snapshots that are relevant for the node * @param config important snapshot related configuration parameters + * @return the initialized instance itself to allow chaining */ - public SnapshotServiceImpl(Tangle tangle, SnapshotProvider snapshotProvider, SnapshotConfig config) { + public SnapshotServiceImpl init(Tangle tangle, SnapshotProvider snapshotProvider, SnapshotConfig config) { this.tangle = tangle; this.snapshotProvider = snapshotProvider; this.config = config; + + return this; } /** diff --git a/src/main/java/com/iota/iri/service/tipselection/impl/CumulativeWeightCalculator.java b/src/main/java/com/iota/iri/service/tipselection/impl/CumulativeWeightCalculator.java index 185ae75a28..263827f4ed 100644 --- a/src/main/java/com/iota/iri/service/tipselection/impl/CumulativeWeightCalculator.java +++ b/src/main/java/com/iota/iri/service/tipselection/impl/CumulativeWeightCalculator.java @@ -5,7 +5,7 @@ import com.iota.iri.model.Hash; import com.iota.iri.model.HashId; import com.iota.iri.model.HashPrefix; -import com.iota.iri.service.snapshot.Snapshot; +import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.service.tipselection.RatingCalculator; import com.iota.iri.utils.collections.impl.TransformingBoundedHashSet; import com.iota.iri.storage.Tangle; @@ -31,11 +31,11 @@ public class CumulativeWeightCalculator implements RatingCalculator{ public static final int MAX_FUTURE_SET_SIZE = 5000; public final Tangle tangle; - private final Snapshot initialSnapshot; + private final SnapshotProvider snapshotProvider; - public CumulativeWeightCalculator(Tangle tangle, Snapshot initialSnapshot) { + public CumulativeWeightCalculator(Tangle tangle, SnapshotProvider snapshotProvider) { this.tangle = tangle; - this.initialSnapshot = initialSnapshot; + this.snapshotProvider = snapshotProvider; } @Override @@ -89,7 +89,7 @@ private Collection getTxDirectApproversHashes(Hash txHash, Map(appHashes.size()); for (Hash appHash : appHashes) { //if not genesis (the tx that confirms itself) - if (!initialSnapshot.hasSolidEntryPoint(appHash)) { + if (!snapshotProvider.getInitialSnapshot().hasSolidEntryPoint(appHash)) { txApprovers.add(appHash); } } diff --git a/src/main/java/com/iota/iri/service/tipselection/impl/EntryPointSelectorImpl.java b/src/main/java/com/iota/iri/service/tipselection/impl/EntryPointSelectorImpl.java index 6ad1ec78fc..67e81ab04f 100644 --- a/src/main/java/com/iota/iri/service/tipselection/impl/EntryPointSelectorImpl.java +++ b/src/main/java/com/iota/iri/service/tipselection/impl/EntryPointSelectorImpl.java @@ -1,8 +1,8 @@ package com.iota.iri.service.tipselection.impl; -import com.iota.iri.MilestoneTracker; import com.iota.iri.controllers.MilestoneViewModel; import com.iota.iri.model.Hash; +import com.iota.iri.service.milestone.LatestMilestoneTracker; import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.service.tipselection.EntryPointSelector; import com.iota.iri.storage.Tangle; @@ -16,24 +16,26 @@ public class EntryPointSelectorImpl implements EntryPointSelector { private final Tangle tangle; private final SnapshotProvider snapshotProvider; - private final MilestoneTracker milestoneTracker; + private final LatestMilestoneTracker latestMilestoneTracker; + + public EntryPointSelectorImpl(Tangle tangle, SnapshotProvider snapshotProvider, + LatestMilestoneTracker latestMilestoneTracker) { - public EntryPointSelectorImpl(Tangle tangle, SnapshotProvider snapshotProvider, MilestoneTracker milestoneTracker) { this.tangle = tangle; this.snapshotProvider = snapshotProvider; - this.milestoneTracker = milestoneTracker; + this.latestMilestoneTracker = latestMilestoneTracker; } @Override public Hash getEntryPoint(int depth) throws Exception { - int milestoneIndex = Math.max(milestoneTracker.latestSolidSubtangleMilestoneIndex - depth - 1, + int milestoneIndex = Math.max(snapshotProvider.getLatestSnapshot().getIndex() - depth - 1, snapshotProvider.getInitialSnapshot().getIndex()); MilestoneViewModel milestoneViewModel = MilestoneViewModel.findClosestNextMilestone(tangle, milestoneIndex, - milestoneTracker.latestMilestoneIndex); + latestMilestoneTracker.getLatestMilestoneIndex()); if (milestoneViewModel != null && milestoneViewModel.getHash() != null) { return milestoneViewModel.getHash(); } - return milestoneTracker.latestSolidSubtangleMilestone; + return snapshotProvider.getLatestSnapshot().getHash(); } } diff --git a/src/main/java/com/iota/iri/service/tipselection/impl/TipSelectorImpl.java b/src/main/java/com/iota/iri/service/tipselection/impl/TipSelectorImpl.java index 83add177b6..8c3f844472 100644 --- a/src/main/java/com/iota/iri/service/tipselection/impl/TipSelectorImpl.java +++ b/src/main/java/com/iota/iri/service/tipselection/impl/TipSelectorImpl.java @@ -5,6 +5,7 @@ import com.iota.iri.conf.TipSelConfig; import com.iota.iri.model.Hash; import com.iota.iri.model.HashId; +import com.iota.iri.service.ledger.LedgerService; import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.service.tipselection.*; import com.iota.iri.storage.Tangle; @@ -29,19 +30,17 @@ public class TipSelectorImpl implements TipSelector { private final RatingCalculator ratingCalculator; private final Walker walker; - private final LedgerValidator ledgerValidator; + private final LedgerService ledgerService; private final Tangle tangle; private final SnapshotProvider snapshotProvider; - private final MilestoneTracker milestoneTracker; private final TipSelConfig config; public TipSelectorImpl(Tangle tangle, SnapshotProvider snapshotProvider, - LedgerValidator ledgerValidator, + LedgerService ledgerService, EntryPointSelector entryPointSelector, RatingCalculator ratingCalculator, Walker walkerAlpha, - MilestoneTracker milestoneTracker, TipSelConfig config) { this.entryPointSelector = entryPointSelector; @@ -50,10 +49,9 @@ public TipSelectorImpl(Tangle tangle, this.walker = walkerAlpha; //used by walkValidator - this.ledgerValidator = ledgerValidator; + this.ledgerService = ledgerService; this.tangle = tangle; this.snapshotProvider = snapshotProvider; - this.milestoneTracker = milestoneTracker; this.config = config; } @@ -76,7 +74,7 @@ public TipSelectorImpl(Tangle tangle, @Override public List getTransactionsToApprove(int depth, Optional reference) throws Exception { try { - milestoneTracker.latestSnapshot.rwlock.readLock().lock(); + snapshotProvider.getLatestSnapshot().lockRead(); //preparation Hash entryPoint = entryPointSelector.getEntryPoint(depth); @@ -84,7 +82,7 @@ public List getTransactionsToApprove(int depth, Optional reference) //random walk List tips = new LinkedList<>(); - WalkValidator walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerValidator, milestoneTracker, config); + WalkValidator walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerService, config); Hash tip = walker.walk(entryPoint, rating, walkValidator); tips.add(tip); @@ -98,13 +96,13 @@ public List getTransactionsToApprove(int depth, Optional reference) tips.add(tip); //validate - if (!ledgerValidator.checkConsistency(tips)) { + if (!ledgerService.tipsConsistent(tips)) { throw new IllegalStateException(TIPS_NOT_CONSISTENT); } return tips; } finally { - milestoneTracker.latestSnapshot.rwlock.readLock().unlock(); + snapshotProvider.getLatestSnapshot().unlockRead(); } } diff --git a/src/main/java/com/iota/iri/service/tipselection/impl/WalkValidatorImpl.java b/src/main/java/com/iota/iri/service/tipselection/impl/WalkValidatorImpl.java index 3611a1de19..e598639c49 100644 --- a/src/main/java/com/iota/iri/service/tipselection/impl/WalkValidatorImpl.java +++ b/src/main/java/com/iota/iri/service/tipselection/impl/WalkValidatorImpl.java @@ -5,6 +5,7 @@ import com.iota.iri.conf.TipSelConfig; import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.model.Hash; +import com.iota.iri.service.ledger.LedgerService; import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.service.tipselection.WalkValidator; import com.iota.iri.storage.Tangle; @@ -30,8 +31,7 @@ public class WalkValidatorImpl implements WalkValidator { private final Tangle tangle; private final Logger log = LoggerFactory.getLogger(WalkValidator.class); private final SnapshotProvider snapshotProvider; - private final LedgerValidator ledgerValidator; - private final MilestoneTracker milestoneTracker; + private final LedgerService ledgerService; private final TipSelConfig config; @@ -39,11 +39,10 @@ public class WalkValidatorImpl implements WalkValidator { private Map myDiff; private Set myApprovedHashes; - public WalkValidatorImpl(Tangle tangle, SnapshotProvider snapshotProvider, LedgerValidator ledgerValidator, MilestoneTracker milestoneTracker, TipSelConfig config) { + public WalkValidatorImpl(Tangle tangle, SnapshotProvider snapshotProvider, LedgerService ledgerService, TipSelConfig config) { this.tangle = tangle; this.snapshotProvider = snapshotProvider; - this.ledgerValidator = ledgerValidator; - this.milestoneTracker = milestoneTracker; + this.ledgerService = ledgerService; this.config = config; maxDepthOkMemoization = new HashSet<>(); @@ -65,10 +64,10 @@ public boolean isValid(Hash transactionHash) throws Exception { log.debug("Validation failed: {} is not solid", transactionHash); return false; } else if (belowMaxDepth(transactionViewModel.getHash(), - milestoneTracker.latestSolidSubtangleMilestoneIndex - config.getMaxDepth())) { + snapshotProvider.getLatestSnapshot().getIndex() - config.getMaxDepth())) { log.debug("Validation failed: {} is below max depth", transactionHash); return false; - } else if (!ledgerValidator.updateDiff(myApprovedHashes, myDiff, transactionViewModel.getHash())) { + } else if (!ledgerService.isBalanceDiffConsistent(myApprovedHashes, myDiff, transactionViewModel.getHash())) { log.debug("Validation failed: {} is not consistent", transactionHash); return false; } diff --git a/src/main/java/com/iota/iri/service/transactionpruning/async/AsyncTransactionPruner.java b/src/main/java/com/iota/iri/service/transactionpruning/async/AsyncTransactionPruner.java index b156533f4c..812b3d0425 100644 --- a/src/main/java/com/iota/iri/service/transactionpruning/async/AsyncTransactionPruner.java +++ b/src/main/java/com/iota/iri/service/transactionpruning/async/AsyncTransactionPruner.java @@ -3,6 +3,7 @@ import com.iota.iri.conf.SnapshotConfig; import com.iota.iri.controllers.TipsViewModel; import com.iota.iri.service.snapshot.Snapshot; +import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.service.transactionpruning.TransactionPruner; import com.iota.iri.service.transactionpruning.TransactionPrunerJob; import com.iota.iri.service.transactionpruning.TransactionPruningException; @@ -21,11 +22,12 @@ import java.util.concurrent.atomic.AtomicInteger; /** - * This class implements the contract of the {@link TransactionPruner} while executing the jobs asynchronously in the - * background. - * - * To start the background processing of pruning tasks one has to make use of the additional {@link #start()} and - * {@link #shutdown()} methods. + * Creates a {@link TransactionPruner} that is able to process it's jobs asynchronously in the background and persists + * its state in a file on the hard disk of the node.
      + *
      + * The asynchronous processing of the jobs is done through {@link Thread}s that are started and stopped by invoking the + * corresponding {@link #start()} and {@link #shutdown()} methods. Since some of the builtin jobs require a special + * logic for the way they are executed, we register the builtin job types here.
      */ public class AsyncTransactionPruner implements TransactionPruner { /** @@ -51,22 +53,22 @@ public class AsyncTransactionPruner implements TransactionPruner { /** * Tangle object which acts as a database interface. */ - private final Tangle tangle; + private Tangle tangle; /** - * Manager for the tips (required for removing pruned transactions from this manager). + * Data provider for the snapshots that are relevant for the node. */ - private final TipsViewModel tipsViewModel; + private SnapshotProvider snapshotProvider; /** - * Last local or global snapshot that acts as a starting point for the state of ledger. + * Manager for the tips (required for removing pruned transactions from this manager). */ - private final Snapshot snapshot; + private TipsViewModel tipsViewModel; /** * Configuration with important snapshot related parameters. */ - private final SnapshotConfig config; + private SnapshotConfig config; /** * Holds a reference to the {@link ThreadIdentifier} for the cleanup thread. @@ -101,24 +103,29 @@ public class AsyncTransactionPruner implements TransactionPruner { private boolean persistRequested = false; /** - * Creates a {@link TransactionPruner} that is able to process it's jobs asynchronously in the background and - * persists its state in a file on the hard disk of the node. - * - * The asynchronous processing of the jobs is done through {@link Thread}s that are started and stopped by invoking - * the corresponding {@link #start()} and {@link #shutdown()} methods. Since some of the builtin jobs require a - * special logic for the way they are executed, we register the builtin job types here. + * This method initializes the instance and registers its dependencies.
      + *
      + * It simply stores the passed in values in their corresponding private properties.
      + *
      + * Note: Instead of handing over the dependencies in the constructor, we register them lazy. This allows us to have + * circular dependencies because the instantiation is separated from the dependency injection. To reduce the + * amount of code that is necessary to correctly instantiate this class, we return the instance itself which + * allows us to still instantiate, initialize and assign in one line - see Example:
      + *
      + * {@code asyncTransactionPruner = new AsyncTransactionPruner().init(...);} * * @param tangle Tangle object which acts as a database interface + * @param snapshotProvider data provider for the snapshots that are relevant for the node * @param tipsViewModel manager for the tips (required for removing pruned transactions from this manager) - * @param snapshot last local or global snapshot that acts as a starting point for the state of ledger * @param config Configuration with important snapshot related configuration parameters + * @return the initialized instance itself to allow chaining */ - public AsyncTransactionPruner(Tangle tangle, TipsViewModel tipsViewModel, Snapshot snapshot, + public AsyncTransactionPruner init(Tangle tangle, SnapshotProvider snapshotProvider, TipsViewModel tipsViewModel, SnapshotConfig config) { this.tangle = tangle; + this.snapshotProvider = snapshotProvider; this.tipsViewModel = tipsViewModel; - this.snapshot = snapshot; this.config = config; addJobQueue(UnconfirmedSubtanglePrunerJob.class, new SimpleJobQueue(this)); @@ -126,6 +133,8 @@ public AsyncTransactionPruner(Tangle tangle, TipsViewModel tipsViewModel, Snapsh registerParser(MilestonePrunerJob.class, MilestonePrunerJob::parse); registerParser(UnconfirmedSubtanglePrunerJob.class, UnconfirmedSubtanglePrunerJob::parse); + + return this; } /** @@ -138,7 +147,7 @@ public void addJob(TransactionPrunerJob job) throws TransactionPruningException job.setTransactionPruner(this); job.setTangle(tangle); job.setTipsViewModel(tipsViewModel); - job.setSnapshot(snapshot); + job.setSnapshot(snapshotProvider.getInitialSnapshot()); // this call is "unchecked" to a "raw" JobQueue and it is intended since the matching JobQueue is defined by the // registered job types diff --git a/src/main/java/com/iota/iri/utils/thread/DedicatedScheduledExecutorService.java b/src/main/java/com/iota/iri/utils/thread/DedicatedScheduledExecutorService.java index 33d5cef5c2..d0dd4923ab 100644 --- a/src/main/java/com/iota/iri/utils/thread/DedicatedScheduledExecutorService.java +++ b/src/main/java/com/iota/iri/utils/thread/DedicatedScheduledExecutorService.java @@ -235,12 +235,9 @@ public void onStartTask(TaskDetails taskDetails) { super.onStartTask(taskDetails); if (debug || (threadName != null && taskDetails.getExecutionCount().get() == 0)) { - String oldThreadName = Thread.currentThread().getName(); Thread.currentThread().setName(taskDetails.getThreadName()); printStartedMessage(taskDetails); - - Thread.currentThread().setName(oldThreadName); } if (threadName != null) { diff --git a/src/test/java/com/iota/iri/BundleValidatorTest.java b/src/test/java/com/iota/iri/BundleValidatorTest.java index 2d5d506224..b6f2387f0e 100644 --- a/src/test/java/com/iota/iri/BundleValidatorTest.java +++ b/src/test/java/com/iota/iri/BundleValidatorTest.java @@ -30,7 +30,7 @@ public static void setUp() throws Exception { logFolder.create(); tangle.addPersistenceProvider(new RocksDBPersistenceProvider(dbFolder.getRoot().getAbsolutePath(), logFolder.getRoot().getAbsolutePath(),1000)); tangle.init(); - snapshotProvider = new SnapshotProviderImpl(new MainnetConfig()); + snapshotProvider = new SnapshotProviderImpl().init(new MainnetConfig()); } @AfterClass diff --git a/src/test/java/com/iota/iri/MilestoneTrackerTest.java b/src/test/java/com/iota/iri/MilestoneTrackerTest.java index 571bdfe4a3..43902be78e 100644 --- a/src/test/java/com/iota/iri/MilestoneTrackerTest.java +++ b/src/test/java/com/iota/iri/MilestoneTrackerTest.java @@ -43,7 +43,7 @@ public static void tearDown() throws Exception { @BeforeClass public static void beforeClass() throws Exception { tangle = new Tangle(); - snapshotProvider = new SnapshotProviderImpl(new MainnetConfig()); + snapshotProvider = new SnapshotProviderImpl().init(new MainnetConfig()); dbFolder.create(); logFolder.create(); tangle.addPersistenceProvider(new RocksDBPersistenceProvider(dbFolder.getRoot().getAbsolutePath(), logFolder @@ -63,7 +63,7 @@ private static void initializeMilestoneTracker(String coordinator, int keys, int configuration.parseConfigFromArgs(args); MessageQ messageQ = Mockito.mock(MessageQ.class); - TransactionValidator transactionValidator = new TransactionValidator(tangle, snapshotProvider.getInitialSnapshot(), new TipsViewModel(), new TransactionRequester(tangle, snapshotProvider.getInitialSnapshot(), messageQ)); + TransactionValidator transactionValidator = new TransactionValidator(tangle, snapshotProvider, new TipsViewModel(), new TransactionRequester(tangle, snapshotProvider, messageQ)); milestoneTracker = new MilestoneTracker(tangle, snapshotProvider, transactionValidator, messageQ, Snapshot.init(configuration).clone(), configuration); } diff --git a/src/test/java/com/iota/iri/TransactionValidatorTest.java b/src/test/java/com/iota/iri/TransactionValidatorTest.java index 56064d4cc4..e64162ffbc 100644 --- a/src/test/java/com/iota/iri/TransactionValidatorTest.java +++ b/src/test/java/com/iota/iri/TransactionValidatorTest.java @@ -36,15 +36,15 @@ public static void setUp() throws Exception { dbFolder.create(); logFolder.create(); tangle = new Tangle(); - snapshotProvider = new SnapshotProviderImpl(new MainnetConfig()); + snapshotProvider = new SnapshotProviderImpl().init(new MainnetConfig()); tangle.addPersistenceProvider( new RocksDBPersistenceProvider( dbFolder.getRoot().getAbsolutePath(), logFolder.getRoot().getAbsolutePath(),1000)); tangle.init(); TipsViewModel tipsViewModel = new TipsViewModel(); MessageQ messageQ = Mockito.mock(MessageQ.class); - TransactionRequester txRequester = new TransactionRequester(tangle, snapshotProvider.getInitialSnapshot(), messageQ); - txValidator = new TransactionValidator(tangle, snapshotProvider.getInitialSnapshot(), tipsViewModel, txRequester); + TransactionRequester txRequester = new TransactionRequester(tangle, snapshotProvider, messageQ); + txValidator = new TransactionValidator(tangle, snapshotProvider, tipsViewModel, txRequester); txValidator.setMwm(false, MAINNET_MWM); } diff --git a/src/test/java/com/iota/iri/benchmarks/dbbenchmark/states/DbState.java b/src/test/java/com/iota/iri/benchmarks/dbbenchmark/states/DbState.java index 5047f69c73..a604c98b02 100644 --- a/src/test/java/com/iota/iri/benchmarks/dbbenchmark/states/DbState.java +++ b/src/test/java/com/iota/iri/benchmarks/dbbenchmark/states/DbState.java @@ -45,7 +45,7 @@ public void setup() throws Exception { BaseIotaConfig.Defaults.DB_CACHE_SIZE); dbProvider.init(); tangle = new Tangle(); - snapshotProvider = new SnapshotProviderImpl(new MainnetConfig()); + snapshotProvider = new SnapshotProviderImpl().init(new MainnetConfig()); tangle.addPersistenceProvider(dbProvider); String trytes = ""; System.out.println("numTxsToTest = [" + numTxsToTest + "]"); diff --git a/src/test/java/com/iota/iri/controllers/MilestoneViewModelTest.java b/src/test/java/com/iota/iri/controllers/MilestoneViewModelTest.java index 72080ecf68..354796f087 100644 --- a/src/test/java/com/iota/iri/controllers/MilestoneViewModelTest.java +++ b/src/test/java/com/iota/iri/controllers/MilestoneViewModelTest.java @@ -1,10 +1,8 @@ package com.iota.iri.controllers; import com.iota.iri.conf.MainnetConfig; -import com.iota.iri.conf.SnapshotConfig; import com.iota.iri.model.Hash; import com.iota.iri.model.HashFactory; -import com.iota.iri.model.TransactionHash; import com.iota.iri.storage.Tangle; import com.iota.iri.storage.rocksDB.RocksDBPersistenceProvider; import org.junit.*; @@ -175,8 +173,7 @@ public void nextGreaterThan() throws Exception { int next = first + 1; new MilestoneViewModel(next, HashFactory.TRANSACTION.create("GBCDEBGHIJKLMNOPQRSTUVWXYZ9ABCDEFGHIJKLMNOPQRSTUVWXYZ9ABCDEFGHIJKLMNOPQRSTUV99999")).store(tangle); new MilestoneViewModel(first, HashFactory.TRANSACTION.create("GBCDEFGHIJKLMNODQRSTUVWXYZ9ABCDEFGHIJKLMNOPQRSTUVWXYZ9ABCDEFGHIJKLMNOPQRSTUV99999")).store(tangle); - assertEquals(next, MilestoneViewModel.findClosestNextMilestone( - tangle, first, next).index().intValue()); + assertEquals("the found milestone should be following the previous one", next, MilestoneViewModel.findClosestNextMilestone(tangle, first, next).index().intValue()); } @Test diff --git a/src/test/java/com/iota/iri/controllers/TransactionRequesterTest.java b/src/test/java/com/iota/iri/controllers/TransactionRequesterTest.java index 5c5e5d37b9..23f1e5320c 100644 --- a/src/test/java/com/iota/iri/controllers/TransactionRequesterTest.java +++ b/src/test/java/com/iota/iri/controllers/TransactionRequesterTest.java @@ -23,7 +23,7 @@ public class TransactionRequesterTest { @Before public void setUp() throws Exception { - snapshotProvider = new SnapshotProviderImpl(new MainnetConfig()); + snapshotProvider = new SnapshotProviderImpl().init(new MainnetConfig()); } @After @@ -78,7 +78,7 @@ public void instance() throws Exception { @Test public void nonMilestoneCapacityLimited() throws Exception { - TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider.getInitialSnapshot(), mq); + TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider, mq); int capacity = TransactionRequester.MAX_TX_REQ_QUEUE_SIZE; //fill tips list for (int i = 0; i < capacity * 2 ; i++) { @@ -91,7 +91,7 @@ public void nonMilestoneCapacityLimited() throws Exception { @Test public void milestoneCapacityNotLimited() throws Exception { - TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider.getInitialSnapshot(), mq); + TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider, mq); int capacity = TransactionRequester.MAX_TX_REQ_QUEUE_SIZE; //fill tips list for (int i = 0; i < capacity * 2 ; i++) { @@ -104,7 +104,7 @@ public void milestoneCapacityNotLimited() throws Exception { @Test public void mixedCapacityLimited() throws Exception { - TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider.getInitialSnapshot(), mq); + TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider, mq); int capacity = TransactionRequester.MAX_TX_REQ_QUEUE_SIZE; //fill tips list for (int i = 0; i < capacity * 4 ; i++) { diff --git a/src/test/java/com/iota/iri/controllers/TransactionViewModelTest.java b/src/test/java/com/iota/iri/controllers/TransactionViewModelTest.java index d214a585bf..b3ae7f91c6 100644 --- a/src/test/java/com/iota/iri/controllers/TransactionViewModelTest.java +++ b/src/test/java/com/iota/iri/controllers/TransactionViewModelTest.java @@ -45,7 +45,7 @@ public static void setUp() throws Exception { logFolder.getRoot().getAbsolutePath(),1000); tangle.addPersistenceProvider(rocksDBPersistenceProvider); tangle.init(); - snapshotProvider = new SnapshotProviderImpl(new MainnetConfig()); + snapshotProvider = new SnapshotProviderImpl().init(new MainnetConfig()); } @AfterClass diff --git a/src/test/java/com/iota/iri/service/tipselection/impl/CumulativeWeightCalculatorTest.java b/src/test/java/com/iota/iri/service/tipselection/impl/CumulativeWeightCalculatorTest.java index 98cd63ef2f..94bc7171c1 100644 --- a/src/test/java/com/iota/iri/service/tipselection/impl/CumulativeWeightCalculatorTest.java +++ b/src/test/java/com/iota/iri/service/tipselection/impl/CumulativeWeightCalculatorTest.java @@ -45,13 +45,13 @@ public static void tearDown() throws Exception { @BeforeClass public static void setUp() throws Exception { tangle = new Tangle(); - snapshotProvider = new SnapshotProviderImpl(new MainnetConfig()); + snapshotProvider = new SnapshotProviderImpl().init(new MainnetConfig()); dbFolder.create(); logFolder.create(); tangle.addPersistenceProvider(new RocksDBPersistenceProvider(dbFolder.getRoot().getAbsolutePath(), logFolder .getRoot().getAbsolutePath(), 1000)); tangle.init(); - cumulativeWeightCalculator = new CumulativeWeightCalculator(tangle, snapshotProvider.getInitialSnapshot()); + cumulativeWeightCalculator = new CumulativeWeightCalculator(tangle, snapshotProvider); } @Test diff --git a/src/test/java/com/iota/iri/service/tipselection/impl/EntryPointSelectorImplTest.java b/src/test/java/com/iota/iri/service/tipselection/impl/EntryPointSelectorImplTest.java index 28274663d5..70eb49a7d6 100644 --- a/src/test/java/com/iota/iri/service/tipselection/impl/EntryPointSelectorImplTest.java +++ b/src/test/java/com/iota/iri/service/tipselection/impl/EntryPointSelectorImplTest.java @@ -7,6 +7,7 @@ import com.iota.iri.model.Hash; import com.iota.iri.model.IntegerIndex; import com.iota.iri.model.TransactionHash; +import com.iota.iri.service.milestone.LatestMilestoneTracker; import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.service.snapshot.impl.SnapshotProviderImpl; import com.iota.iri.service.tipselection.EntryPointSelector; @@ -26,14 +27,14 @@ public class EntryPointSelectorImplTest { @Mock - private MilestoneTracker milestoneTracker; + private LatestMilestoneTracker latestMilestoneTracker; @Mock private Tangle tangle; private static SnapshotProvider snapshotProvider; @BeforeClass public static void setUp() throws Exception { - snapshotProvider = new SnapshotProviderImpl(new MainnetConfig()); + snapshotProvider = new SnapshotProviderImpl().init(new MainnetConfig()); MilestoneViewModel.clear(); } @@ -43,7 +44,7 @@ public void testEntryPointBWithTangleData() throws Exception { mockTangleBehavior(milestoneHash); mockMilestoneTrackerBehavior(snapshotProvider.getInitialSnapshot().getIndex() + 1, Hash.NULL_HASH); - EntryPointSelector entryPointSelector = new EntryPointSelectorImpl(tangle, snapshotProvider, milestoneTracker); + EntryPointSelector entryPointSelector = new EntryPointSelectorImpl(tangle, snapshotProvider, latestMilestoneTracker); Hash entryPoint = entryPointSelector.getEntryPoint(10); Assert.assertEquals("The entry point should be the milestone in the Tangle", milestoneHash, entryPoint); @@ -53,7 +54,7 @@ public void testEntryPointBWithTangleData() throws Exception { public void testEntryPointAWithoutTangleData() throws Exception { mockMilestoneTrackerBehavior(0, Hash.NULL_HASH); - EntryPointSelector entryPointSelector = new EntryPointSelectorImpl(tangle, snapshotProvider, milestoneTracker); + EntryPointSelector entryPointSelector = new EntryPointSelectorImpl(tangle, snapshotProvider, latestMilestoneTracker); Hash entryPoint = entryPointSelector.getEntryPoint(10); Assert.assertEquals("The entry point should be the last tracked solid milestone", Hash.NULL_HASH, entryPoint); @@ -61,18 +62,15 @@ public void testEntryPointAWithoutTangleData() throws Exception { private void mockMilestoneTrackerBehavior(int latestSolidSubtangleMilestoneIndex, Hash latestSolidSubtangleMilestone) { - milestoneTracker.latestSolidSubtangleMilestoneIndex = latestSolidSubtangleMilestoneIndex; - milestoneTracker.latestMilestoneIndex = latestSolidSubtangleMilestoneIndex; - milestoneTracker.latestSolidSubtangleMilestone = latestSolidSubtangleMilestone; - milestoneTracker.latestMilestone = latestSolidSubtangleMilestone; + snapshotProvider.getLatestSnapshot().setIndex(latestSolidSubtangleMilestoneIndex); + snapshotProvider.getLatestSnapshot().setHash(latestSolidSubtangleMilestone); + Mockito.when(latestMilestoneTracker.getLatestMilestoneIndex()).thenReturn(latestSolidSubtangleMilestoneIndex); } private void mockTangleBehavior(Hash milestoneModelHash) throws Exception { com.iota.iri.model.persistables.Milestone milestoneModel = new com.iota.iri.model.persistables.Milestone(); milestoneModel.index = new IntegerIndex(snapshotProvider.getInitialSnapshot().getIndex() + 1); milestoneModel.hash = milestoneModelHash; - Mockito.when(milestoneTracker.getMilestoneStartIndex()).thenReturn(0); - milestoneTracker.latestMilestoneIndex = 1; Mockito.when(tangle.load(com.iota.iri.model.persistables.Milestone.class, milestoneModel.index)) .thenReturn(milestoneModel); } diff --git a/src/test/java/com/iota/iri/service/tipselection/impl/RatingOneTest.java b/src/test/java/com/iota/iri/service/tipselection/impl/RatingOneTest.java index cc99a27ec4..2c12f945be 100644 --- a/src/test/java/com/iota/iri/service/tipselection/impl/RatingOneTest.java +++ b/src/test/java/com/iota/iri/service/tipselection/impl/RatingOneTest.java @@ -36,7 +36,7 @@ public static void tearDown() throws Exception { @BeforeClass public static void setUp() throws Exception { tangle = new Tangle(); - snapshotProvider = new SnapshotProviderImpl(new MainnetConfig()); + snapshotProvider = new SnapshotProviderImpl().init(new MainnetConfig()); dbFolder.create(); logFolder.create(); tangle.addPersistenceProvider(new RocksDBPersistenceProvider(dbFolder.getRoot().getAbsolutePath(), logFolder diff --git a/src/test/java/com/iota/iri/service/tipselection/impl/TailFinderImplTest.java b/src/test/java/com/iota/iri/service/tipselection/impl/TailFinderImplTest.java index 06581f76b4..281e8fe710 100644 --- a/src/test/java/com/iota/iri/service/tipselection/impl/TailFinderImplTest.java +++ b/src/test/java/com/iota/iri/service/tipselection/impl/TailFinderImplTest.java @@ -40,7 +40,7 @@ public static void tearDown() throws Exception { @BeforeClass public static void setUp() throws Exception { tangle = new Tangle(); - snapshotProvider = new SnapshotProviderImpl(new MainnetConfig()); + snapshotProvider = new SnapshotProviderImpl().init(new MainnetConfig()); dbFolder.create(); logFolder.create(); tangle.addPersistenceProvider(new RocksDBPersistenceProvider(dbFolder.getRoot().getAbsolutePath(), logFolder diff --git a/src/test/java/com/iota/iri/service/tipselection/impl/WalkValidatorImplTest.java b/src/test/java/com/iota/iri/service/tipselection/impl/WalkValidatorImplTest.java index fbf5a81316..082a76e3a6 100644 --- a/src/test/java/com/iota/iri/service/tipselection/impl/WalkValidatorImplTest.java +++ b/src/test/java/com/iota/iri/service/tipselection/impl/WalkValidatorImplTest.java @@ -1,14 +1,12 @@ package com.iota.iri.service.tipselection.impl; -import com.iota.iri.LedgerValidator; -import com.iota.iri.MilestoneTracker; import com.iota.iri.TransactionTestUtils; -import com.iota.iri.TransactionValidator; import com.iota.iri.conf.MainnetConfig; import com.iota.iri.conf.TipSelConfig; import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.controllers.TransactionViewModelTest; import com.iota.iri.model.Hash; +import com.iota.iri.service.ledger.LedgerService; import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.service.snapshot.impl.SnapshotProviderImpl; import com.iota.iri.storage.Tangle; @@ -35,11 +33,7 @@ public class WalkValidatorImplTest { private static SnapshotProvider snapshotProvider; private TipSelConfig config = new MainnetConfig(); @Mock - private LedgerValidator ledgerValidator; - @Mock - private TransactionValidator transactionValidator; - @Mock - private MilestoneTracker milestoneTrackerTracker; + private static LedgerService ledgerService; @AfterClass public static void tearDown() throws Exception { @@ -52,7 +46,7 @@ public static void tearDown() throws Exception { @BeforeClass public static void setUp() throws Exception { tangle = new Tangle(); - snapshotProvider = new SnapshotProviderImpl(new MainnetConfig()); + snapshotProvider = new SnapshotProviderImpl().init(new MainnetConfig()); dbFolder.create(); logFolder.create(); tangle.addPersistenceProvider(new RocksDBPersistenceProvider(dbFolder.getRoot().getAbsolutePath(), logFolder @@ -67,11 +61,11 @@ public void shouldPassValidation() throws Exception { tx.updateSolid(true); tx.store(tangle, snapshotProvider.getInitialSnapshot()); Hash hash = tx.getHash(); - Mockito.when(ledgerValidator.updateDiff(new HashSet<>(), new HashMap<>(), hash)) + Mockito.when(ledgerService.isBalanceDiffConsistent(new HashSet<>(), new HashMap<>(), hash)) .thenReturn(true); - milestoneTrackerTracker.latestSolidSubtangleMilestoneIndex = depth; + snapshotProvider.getLatestSnapshot().setIndex(depth); - WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerValidator, milestoneTrackerTracker, config); + WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerService, config); Assert.assertTrue("Validation failed", walkValidator.isValid(hash)); } @@ -82,12 +76,12 @@ public void failOnTxType() throws Exception { tx.store(tangle, snapshotProvider.getInitialSnapshot()); Hash hash = tx.getTrunkTransactionHash(); tx.updateSolid(true); - Mockito.when(ledgerValidator.updateDiff(new HashSet<>(), new HashMap<>(), hash)) + Mockito.when(ledgerService.isBalanceDiffConsistent(new HashSet<>(), new HashMap<>(), hash)) .thenReturn(true); - milestoneTrackerTracker.latestSolidSubtangleMilestoneIndex = depth; + snapshotProvider.getLatestSnapshot().setIndex(depth); - WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerValidator, - milestoneTrackerTracker, config); + WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerService, + config); Assert.assertFalse("Validation succeded but should have failed since tx is missing", walkValidator.isValid(hash)); } @@ -97,12 +91,12 @@ public void failOnTxIndex() throws Exception { tx.store(tangle, snapshotProvider.getInitialSnapshot()); Hash hash = tx.getHash(); tx.updateSolid(true); - Mockito.when(ledgerValidator.updateDiff(new HashSet<>(), new HashMap<>(), hash)) + Mockito.when(ledgerService.isBalanceDiffConsistent(new HashSet<>(), new HashMap<>(), hash)) .thenReturn(true); - milestoneTrackerTracker.latestSolidSubtangleMilestoneIndex = Integer.MAX_VALUE; + snapshotProvider.getLatestSnapshot().setIndex(Integer.MAX_VALUE); - WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerValidator, - milestoneTrackerTracker, config); + WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerService, + config); Assert.assertFalse("Validation succeded but should have failed since we are not on a tail", walkValidator.isValid(hash)); } @@ -112,12 +106,12 @@ public void failOnSolid() throws Exception { tx.store(tangle, snapshotProvider.getInitialSnapshot()); Hash hash = tx.getHash(); tx.updateSolid(false); - Mockito.when(ledgerValidator.updateDiff(new HashSet<>(), new HashMap<>(), hash)) + Mockito.when(ledgerService.isBalanceDiffConsistent(new HashSet<>(), new HashMap<>(), hash)) .thenReturn(true); - milestoneTrackerTracker.latestSolidSubtangleMilestoneIndex = Integer.MAX_VALUE; + snapshotProvider.getLatestSnapshot().setIndex(Integer.MAX_VALUE); - WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerValidator, - milestoneTrackerTracker, config); + WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerService, + config); Assert.assertFalse("Validation succeded but should have failed since tx is not solid", walkValidator.isValid(hash)); } @@ -129,11 +123,11 @@ public void failOnBelowMaxDepthDueToOldMilestone() throws Exception { tx.setSnapshot(tangle, snapshotProvider.getInitialSnapshot(), 2); Hash hash = tx.getHash(); tx.updateSolid(true); - Mockito.when(ledgerValidator.updateDiff(new HashSet<>(), new HashMap<>(), hash)) + Mockito.when(ledgerService.isBalanceDiffConsistent(new HashSet<>(), new HashMap<>(), hash)) .thenReturn(true); - milestoneTrackerTracker.latestSolidSubtangleMilestoneIndex = Integer.MAX_VALUE; - WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerValidator, - milestoneTrackerTracker, config); + snapshotProvider.getLatestSnapshot().setIndex(Integer.MAX_VALUE); + WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerService, + config); Assert.assertFalse("Validation succeeded but should have failed tx is below max depth", walkValidator.isValid(hash)); } @@ -152,11 +146,11 @@ public void belowMaxDepthWithFreshMilestone() throws Exception { hash = tx.getHash(); tx.store(tangle, snapshotProvider.getInitialSnapshot()); } - Mockito.when(ledgerValidator.updateDiff(new HashSet<>(), new HashMap<>(), hash)) + Mockito.when(ledgerService.isBalanceDiffConsistent(new HashSet<>(), new HashMap<>(), hash)) .thenReturn(true); - milestoneTrackerTracker.latestSolidSubtangleMilestoneIndex = 100; - WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerValidator, - milestoneTrackerTracker, config); + snapshotProvider.getLatestSnapshot().setIndex(100); + WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerService, + config); Assert.assertTrue("Validation failed but should have succeeded since tx is above max depth", walkValidator.isValid(hash)); } @@ -177,11 +171,11 @@ public void failBelowMaxDepthWithFreshMilestoneDueToLongChain() throws Exception tx.updateSolid(true); tx.store(tangle, snapshotProvider.getInitialSnapshot()); } - Mockito.when(ledgerValidator.updateDiff(new HashSet<>(), new HashMap<>(), hash)) + Mockito.when(ledgerService.isBalanceDiffConsistent(new HashSet<>(), new HashMap<>(), hash)) .thenReturn(true); - milestoneTrackerTracker.latestSolidSubtangleMilestoneIndex = 100; - WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerValidator, - milestoneTrackerTracker, config); + snapshotProvider.getLatestSnapshot().setIndex(100); + WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerService, + config); Assert.assertFalse("Validation succeeded but should have failed since tx is below max depth", walkValidator.isValid(hash)); } @@ -199,11 +193,11 @@ public void belowMaxDepthOnGenesis() throws Exception { hash = tx.getHash(); tx.store(tangle, snapshotProvider.getInitialSnapshot()); } - Mockito.when(ledgerValidator.updateDiff(new HashSet<>(), new HashMap<>(), tx.getHash())) + Mockito.when(ledgerService.isBalanceDiffConsistent(new HashSet<>(), new HashMap<>(), tx.getHash())) .thenReturn(true); - milestoneTrackerTracker.latestSolidSubtangleMilestoneIndex = 15; - WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerValidator, - milestoneTrackerTracker, config); + snapshotProvider.getLatestSnapshot().setIndex(15); + WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerService, + config); Assert.assertTrue("Validation failed but should have succeeded. We didn't exceed the maximal amount of" + "transactions that may be analyzed.", walkValidator.isValid(tx.getHash())); @@ -223,11 +217,11 @@ public void failBelowMaxDepthOnGenesisDueToLongChain() throws Exception { tx.store(tangle, snapshotProvider.getInitialSnapshot()); hash = tx.getHash(); } - Mockito.when(ledgerValidator.updateDiff(new HashSet<>(), new HashMap<>(), tx.getHash())) + Mockito.when(ledgerService.isBalanceDiffConsistent(new HashSet<>(), new HashMap<>(), tx.getHash())) .thenReturn(true); - milestoneTrackerTracker.latestSolidSubtangleMilestoneIndex = 17; - WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerValidator, - milestoneTrackerTracker, config); + snapshotProvider.getLatestSnapshot().setIndex(17); + WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerService, + config); Assert.assertFalse("Validation succeeded but should have failed. We exceeded the maximal amount of" + "transactions that may be analyzed.", walkValidator.isValid(tx.getHash())); @@ -239,12 +233,12 @@ public void failOnInconsistency() throws Exception { tx.store(tangle, snapshotProvider.getInitialSnapshot()); Hash hash = tx.getHash(); tx.updateSolid(true); - Mockito.when(ledgerValidator.updateDiff(new HashSet<>(), new HashMap<>(), hash)) + Mockito.when(ledgerService.isBalanceDiffConsistent(new HashSet<>(), new HashMap<>(), hash)) .thenReturn(false); - milestoneTrackerTracker.latestSolidSubtangleMilestoneIndex = Integer.MAX_VALUE; + snapshotProvider.getLatestSnapshot().setIndex(Integer.MAX_VALUE); - WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerValidator, - milestoneTrackerTracker, config); + WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerService, + config); Assert.assertFalse("Validation succeded but should have failed due to inconsistent ledger state", walkValidator.isValid(hash)); } @@ -281,14 +275,14 @@ public void dontMarkWrongTxsAsBelowMaxDepth() throws Exception { tx4.updateSolid(true); tx4.store(tangle, snapshotProvider.getInitialSnapshot()); - Mockito.when(ledgerValidator.updateDiff(new HashSet<>(), new HashMap<>(), tx4.getHash())) + Mockito.when(ledgerService.isBalanceDiffConsistent(new HashSet<>(), new HashMap<>(), tx4.getHash())) .thenReturn(true); - Mockito.when(ledgerValidator.updateDiff(new HashSet<>(), new HashMap<>(), tx2.getHash())) + Mockito.when(ledgerService.isBalanceDiffConsistent(new HashSet<>(), new HashMap<>(), tx2.getHash())) .thenReturn(true); - milestoneTrackerTracker.latestSolidSubtangleMilestoneIndex = 100; - WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerValidator, - milestoneTrackerTracker, config); + snapshotProvider.getLatestSnapshot().setIndex(100); + WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerService, + config); Assert.assertFalse("Validation of tx4 succeeded but should have failed since tx is below max depth", walkValidator.isValid(tx4.getHash())); Assert.assertTrue("Validation of tx2 failed but should have succeeded since tx is above max depth", @@ -327,12 +321,12 @@ public void allowConfirmedTxToPassBelowMaxDepthAfterMilestoneConfirmation() thro tx4.updateSolid(true); tx4.store(tangle, snapshotProvider.getInitialSnapshot()); - Mockito.when(ledgerValidator.updateDiff(new HashSet<>(), new HashMap<>(), tx4.getHash())) + Mockito.when(ledgerService.isBalanceDiffConsistent(new HashSet<>(), new HashMap<>(), tx4.getHash())) .thenReturn(true); - milestoneTrackerTracker.latestSolidSubtangleMilestoneIndex = 100; - WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerValidator, - milestoneTrackerTracker, config); + snapshotProvider.getLatestSnapshot().setIndex(100); + WalkValidatorImpl walkValidator = new WalkValidatorImpl(tangle, snapshotProvider, ledgerService, + config); Assert.assertFalse("Validation of tx4 succeeded but should have failed since tx is below max depth", walkValidator.isValid(tx4.getHash())); diff --git a/src/test/java/com/iota/iri/service/tipselection/impl/WalkerAlphaTest.java b/src/test/java/com/iota/iri/service/tipselection/impl/WalkerAlphaTest.java index 6d42ee3ea4..3eea85d2ff 100644 --- a/src/test/java/com/iota/iri/service/tipselection/impl/WalkerAlphaTest.java +++ b/src/test/java/com/iota/iri/service/tipselection/impl/WalkerAlphaTest.java @@ -46,7 +46,7 @@ public static void tearDown() throws Exception { @BeforeClass public static void setUp() throws Exception { tangle = new Tangle(); - snapshotProvider = new SnapshotProviderImpl(new MainnetConfig()); + snapshotProvider = new SnapshotProviderImpl().init(new MainnetConfig()); dbFolder.create(); logFolder.create(); tangle.addPersistenceProvider(new RocksDBPersistenceProvider(dbFolder.getRoot().getAbsolutePath(), logFolder diff --git a/src/test/java/com/iota/iri/storage/TangleTest.java b/src/test/java/com/iota/iri/storage/TangleTest.java index 1b7d89d38e..26ff768a6e 100644 --- a/src/test/java/com/iota/iri/storage/TangleTest.java +++ b/src/test/java/com/iota/iri/storage/TangleTest.java @@ -35,7 +35,7 @@ public void setUp() throws Exception { logFolder.getRoot().getAbsolutePath(),1000); tangle.addPersistenceProvider(rocksDBPersistenceProvider); tangle.init(); - snapshotProvider = new SnapshotProviderImpl(new MainnetConfig()); + snapshotProvider = new SnapshotProviderImpl().init(new MainnetConfig()); } @After