From 6c9096c1a496f863f65f491e9c172a419efa848b Mon Sep 17 00:00:00 2001 From: DyrellC Date: Wed, 18 Mar 2020 14:24:28 -0600 Subject: [PATCH 01/18] Move validation to validation service package --- src/main/java/com/iota/iri/Iota.java | 1 + src/main/java/com/iota/iri/MainInjectionConfiguration.java | 1 + .../java/com/iota/iri/controllers/TransactionViewModel.java | 3 ++- .../com/iota/iri/network/NetworkInjectionConfiguration.java | 2 +- .../java/com/iota/iri/network/pipeline/ReceivedStage.java | 2 +- .../network/pipeline/TransactionProcessingPipelineImpl.java | 2 +- .../java/com/iota/iri/network/pipeline/ValidationStage.java | 2 +- src/main/java/com/iota/iri/service/API.java | 2 +- .../iri/service/milestone/impl/MilestoneSolidifierImpl.java | 2 +- .../iri/{ => service/validation}/TransactionValidator.java | 4 ++-- .../java/com/iota/iri/MainInjectionConfigurationTest.java | 1 + .../iota/iri/network/NetworkInjectionConfigurationTest.java | 2 +- .../java/com/iota/iri/network/pipeline/ReceivedStageTest.java | 2 +- .../network/pipeline/TransactionProcessingPipelineTest.java | 2 +- .../com/iota/iri/network/pipeline/ValidationStageTest.java | 2 +- src/test/java/com/iota/iri/service/APITest.java | 2 +- .../{ => service/validation}/TransactionValidatorTest.java | 4 +++- 17 files changed, 21 insertions(+), 15 deletions(-) rename src/main/java/com/iota/iri/{ => service/validation}/TransactionValidator.java (98%) rename src/test/java/com/iota/iri/{ => service/validation}/TransactionValidatorTest.java (98%) diff --git a/src/main/java/com/iota/iri/Iota.java b/src/main/java/com/iota/iri/Iota.java index c901fe8fff..f714603842 100644 --- a/src/main/java/com/iota/iri/Iota.java +++ b/src/main/java/com/iota/iri/Iota.java @@ -22,6 +22,7 @@ import com.iota.iri.service.transactionpruning.DepthPruningCondition; import com.iota.iri.service.transactionpruning.SizePruningCondition; import com.iota.iri.service.transactionpruning.TransactionPruner; +import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.storage.*; import com.iota.iri.storage.rocksDB.RocksDBPersistenceProvider; import com.iota.iri.utils.Pair; diff --git a/src/main/java/com/iota/iri/MainInjectionConfiguration.java b/src/main/java/com/iota/iri/MainInjectionConfiguration.java index e8347fe6f1..f3963bc391 100644 --- a/src/main/java/com/iota/iri/MainInjectionConfiguration.java +++ b/src/main/java/com/iota/iri/MainInjectionConfiguration.java @@ -27,6 +27,7 @@ import com.iota.iri.service.tipselection.impl.*; import com.iota.iri.service.transactionpruning.TransactionPruner; import com.iota.iri.service.transactionpruning.async.AsyncTransactionPruner; +import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.storage.LocalSnapshotsPersistenceProvider; import com.iota.iri.storage.Tangle; import com.iota.iri.storage.rocksDB.RocksDBPersistenceProvider; diff --git a/src/main/java/com/iota/iri/controllers/TransactionViewModel.java b/src/main/java/com/iota/iri/controllers/TransactionViewModel.java index 89e78407ed..207d7cb72d 100644 --- a/src/main/java/com/iota/iri/controllers/TransactionViewModel.java +++ b/src/main/java/com/iota/iri/controllers/TransactionViewModel.java @@ -5,6 +5,7 @@ import com.iota.iri.model.*; import com.iota.iri.model.persistables.*; import com.iota.iri.service.snapshot.Snapshot; +import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.storage.Indexable; import com.iota.iri.storage.Persistable; import com.iota.iri.storage.Tangle; @@ -804,7 +805,7 @@ public static void updateSolidTransactions(Tangle tangle, Snapshot initialSnapsh /** * Updates the {@link Transaction#solid} value of the referenced {@link Transaction} object. * - * Used by the {@link com.iota.iri.TransactionValidator} to quickly set the solidity of a {@link Transaction} set. + * Used by the {@link TransactionValidator} to quickly set the solidity of a {@link Transaction} set. * * @param solid The solidity of the transaction in the database * @return True if the {@link Transaction#solid} has been updated, False if not. diff --git a/src/main/java/com/iota/iri/network/NetworkInjectionConfiguration.java b/src/main/java/com/iota/iri/network/NetworkInjectionConfiguration.java index 032715736d..0bda0a6520 100644 --- a/src/main/java/com/iota/iri/network/NetworkInjectionConfiguration.java +++ b/src/main/java/com/iota/iri/network/NetworkInjectionConfiguration.java @@ -3,7 +3,7 @@ import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.Singleton; -import com.iota.iri.TransactionValidator; +import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.conf.IotaConfig; import com.iota.iri.controllers.TipsViewModel; import com.iota.iri.network.impl.TipsRequesterImpl; diff --git a/src/main/java/com/iota/iri/network/pipeline/ReceivedStage.java b/src/main/java/com/iota/iri/network/pipeline/ReceivedStage.java index e8c5e99711..2f07ce7b75 100644 --- a/src/main/java/com/iota/iri/network/pipeline/ReceivedStage.java +++ b/src/main/java/com/iota/iri/network/pipeline/ReceivedStage.java @@ -1,6 +1,6 @@ package com.iota.iri.network.pipeline; -import com.iota.iri.TransactionValidator; +import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.network.TransactionRequester; import com.iota.iri.network.neighbor.Neighbor; diff --git a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java index 0af71b8420..500fc01611 100644 --- a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java +++ b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java @@ -1,6 +1,6 @@ package com.iota.iri.network.pipeline; -import com.iota.iri.TransactionValidator; +import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.conf.NodeConfig; import com.iota.iri.controllers.TipsViewModel; import com.iota.iri.crypto.batched.BatchedHasher; diff --git a/src/main/java/com/iota/iri/network/pipeline/ValidationStage.java b/src/main/java/com/iota/iri/network/pipeline/ValidationStage.java index a139210eb4..47c41fa3a9 100644 --- a/src/main/java/com/iota/iri/network/pipeline/ValidationStage.java +++ b/src/main/java/com/iota/iri/network/pipeline/ValidationStage.java @@ -1,6 +1,6 @@ package com.iota.iri.network.pipeline; -import com.iota.iri.TransactionValidator; +import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.model.Hash; import com.iota.iri.model.HashFactory; diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java index e3828e9d41..d90376c933 100644 --- a/src/main/java/com/iota/iri/service/API.java +++ b/src/main/java/com/iota/iri/service/API.java @@ -6,7 +6,7 @@ import com.iota.iri.BundleValidator; import com.iota.iri.IRI; import com.iota.iri.IXI; -import com.iota.iri.TransactionValidator; +import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.conf.APIConfig; import com.iota.iri.conf.IotaConfig; import com.iota.iri.controllers.*; 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 9706f1f9df..4b2079fd07 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 @@ -1,6 +1,6 @@ package com.iota.iri.service.milestone.impl; -import com.iota.iri.TransactionValidator; +import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.model.Hash; import com.iota.iri.service.milestone.MilestoneSolidifier; import com.iota.iri.service.snapshot.SnapshotProvider; diff --git a/src/main/java/com/iota/iri/TransactionValidator.java b/src/main/java/com/iota/iri/service/validation/TransactionValidator.java similarity index 98% rename from src/main/java/com/iota/iri/TransactionValidator.java rename to src/main/java/com/iota/iri/service/validation/TransactionValidator.java index ef51b5c203..b2c4995c70 100644 --- a/src/main/java/com/iota/iri/TransactionValidator.java +++ b/src/main/java/com/iota/iri/service/validation/TransactionValidator.java @@ -1,4 +1,4 @@ -package com.iota.iri; +package com.iota.iri.service.validation; import com.google.common.annotations.VisibleForTesting; import com.iota.iri.conf.ProtocolConfig; @@ -65,7 +65,7 @@ public class TransactionValidator { * minimum weight magnitude: the minimal number of 9s that ought to appear at the end of the * transaction hash */ - TransactionValidator(Tangle tangle, SnapshotProvider snapshotProvider, TipsViewModel tipsViewModel, TransactionRequester transactionRequester, ProtocolConfig protocolConfig) { + public TransactionValidator(Tangle tangle, SnapshotProvider snapshotProvider, TipsViewModel tipsViewModel, TransactionRequester transactionRequester, ProtocolConfig protocolConfig) { this.tangle = tangle; this.snapshotProvider = snapshotProvider; this.tipsViewModel = tipsViewModel; diff --git a/src/test/java/com/iota/iri/MainInjectionConfigurationTest.java b/src/test/java/com/iota/iri/MainInjectionConfigurationTest.java index 3695f6cdba..be16d5df2f 100644 --- a/src/test/java/com/iota/iri/MainInjectionConfigurationTest.java +++ b/src/test/java/com/iota/iri/MainInjectionConfigurationTest.java @@ -23,6 +23,7 @@ import com.iota.iri.service.spentaddresses.SpentAddressesProvider; import com.iota.iri.service.spentaddresses.SpentAddressesService; import com.iota.iri.service.transactionpruning.TransactionPruner; +import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.storage.LocalSnapshotsPersistenceProvider; import com.iota.iri.storage.Tangle; import org.junit.Test; diff --git a/src/test/java/com/iota/iri/network/NetworkInjectionConfigurationTest.java b/src/test/java/com/iota/iri/network/NetworkInjectionConfigurationTest.java index c02749a893..6cb4f26b8e 100644 --- a/src/test/java/com/iota/iri/network/NetworkInjectionConfigurationTest.java +++ b/src/test/java/com/iota/iri/network/NetworkInjectionConfigurationTest.java @@ -3,7 +3,7 @@ import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; -import com.iota.iri.TransactionValidator; +import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.conf.BaseIotaConfig; import com.iota.iri.conf.IotaConfig; import com.iota.iri.network.pipeline.TransactionProcessingPipeline; diff --git a/src/test/java/com/iota/iri/network/pipeline/ReceivedStageTest.java b/src/test/java/com/iota/iri/network/pipeline/ReceivedStageTest.java index 772295c4e5..01e9746782 100644 --- a/src/test/java/com/iota/iri/network/pipeline/ReceivedStageTest.java +++ b/src/test/java/com/iota/iri/network/pipeline/ReceivedStageTest.java @@ -1,6 +1,6 @@ package com.iota.iri.network.pipeline; -import com.iota.iri.TransactionValidator; +import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.network.TransactionRequester; import com.iota.iri.network.neighbor.Neighbor; diff --git a/src/test/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineTest.java b/src/test/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineTest.java index b9b8c4ab8f..a88daa900e 100644 --- a/src/test/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineTest.java +++ b/src/test/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineTest.java @@ -1,6 +1,6 @@ package com.iota.iri.network.pipeline; -import com.iota.iri.TransactionValidator; +import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.conf.NodeConfig; import com.iota.iri.controllers.TipsViewModel; import com.iota.iri.network.NeighborRouter; diff --git a/src/test/java/com/iota/iri/network/pipeline/ValidationStageTest.java b/src/test/java/com/iota/iri/network/pipeline/ValidationStageTest.java index 5bd0eecdff..c02d57d3b4 100644 --- a/src/test/java/com/iota/iri/network/pipeline/ValidationStageTest.java +++ b/src/test/java/com/iota/iri/network/pipeline/ValidationStageTest.java @@ -1,6 +1,6 @@ package com.iota.iri.network.pipeline; -import com.iota.iri.TransactionValidator; +import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.model.Hash; import com.iota.iri.network.FIFOCache; diff --git a/src/test/java/com/iota/iri/service/APITest.java b/src/test/java/com/iota/iri/service/APITest.java index f5639156fb..9643959f54 100644 --- a/src/test/java/com/iota/iri/service/APITest.java +++ b/src/test/java/com/iota/iri/service/APITest.java @@ -1,6 +1,6 @@ package com.iota.iri.service; -import com.iota.iri.TransactionValidator; +import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.conf.IotaConfig; import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.service.snapshot.SnapshotProvider; diff --git a/src/test/java/com/iota/iri/TransactionValidatorTest.java b/src/test/java/com/iota/iri/service/validation/TransactionValidatorTest.java similarity index 98% rename from src/test/java/com/iota/iri/TransactionValidatorTest.java rename to src/test/java/com/iota/iri/service/validation/TransactionValidatorTest.java index ad35624978..99a39ea952 100644 --- a/src/test/java/com/iota/iri/TransactionValidatorTest.java +++ b/src/test/java/com/iota/iri/service/validation/TransactionValidatorTest.java @@ -1,5 +1,6 @@ -package com.iota.iri; +package com.iota.iri.service.validation; +import com.iota.iri.TransactionTestUtils; import com.iota.iri.conf.MainnetConfig; import com.iota.iri.conf.ProtocolConfig; @@ -11,6 +12,7 @@ import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.service.snapshot.impl.SnapshotMockUtils; +import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.storage.Tangle; import com.iota.iri.storage.rocksDB.RocksDBPersistenceProvider; import com.iota.iri.utils.Converter; From 029a157957972685f6efce5c8d0b3cfc7b3f342c Mon Sep 17 00:00:00 2001 From: DyrellC Date: Thu, 19 Mar 2020 13:22:21 -0600 Subject: [PATCH 02/18] Add Transaction Solidifier --- src/main/java/com/iota/iri/Iota.java | 36 +- .../iota/iri/MainInjectionConfiguration.java | 54 +-- .../iri/controllers/TransactionViewModel.java | 22 - .../NetworkInjectionConfiguration.java | 11 +- .../iri/network/pipeline/ReceivedStage.java | 24 +- .../iri/network/pipeline/SolidifyPayload.java | 39 ++ .../iri/network/pipeline/SolidifyStage.java | 97 +++++ .../TransactionProcessingPipeline.java | 18 +- .../TransactionProcessingPipelineImpl.java | 56 ++- src/main/java/com/iota/iri/service/API.java | 331 +++++++-------- .../impl/MilestoneSolidifierImpl.java | 28 +- .../validation/TransactionSolidifier.java | 126 ++++++ .../impl/TransactionSolidifierImpl.java | 383 ++++++++++++++++++ 13 files changed, 959 insertions(+), 266 deletions(-) create mode 100644 src/main/java/com/iota/iri/network/pipeline/SolidifyPayload.java create mode 100644 src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java create mode 100644 src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java create mode 100644 src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java diff --git a/src/main/java/com/iota/iri/Iota.java b/src/main/java/com/iota/iri/Iota.java index f714603842..8b0f871ff4 100644 --- a/src/main/java/com/iota/iri/Iota.java +++ b/src/main/java/com/iota/iri/Iota.java @@ -22,6 +22,7 @@ import com.iota.iri.service.transactionpruning.DepthPruningCondition; import com.iota.iri.service.transactionpruning.SizePruningCondition; import com.iota.iri.service.transactionpruning.TransactionPruner; +import com.iota.iri.service.validation.TransactionSolidifier; import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.storage.*; import com.iota.iri.storage.rocksDB.RocksDBPersistenceProvider; @@ -96,6 +97,8 @@ public class Iota { public final MilestoneSolidifier milestoneSolidifier; + public final TransactionSolidifier transactionSolidifier; + public final BundleValidator bundleValidator; public final Tangle tangle; @@ -118,16 +121,16 @@ public class Iota { * */ public Iota(IotaConfig configuration, SpentAddressesProvider spentAddressesProvider, - SpentAddressesService spentAddressesService, SnapshotProvider snapshotProvider, - SnapshotService snapshotService, LocalSnapshotManager localSnapshotManager, - MilestoneService milestoneService, LatestMilestoneTracker latestMilestoneTracker, - LatestSolidMilestoneTracker latestSolidMilestoneTracker, SeenMilestonesRetriever seenMilestonesRetriever, - LedgerService ledgerService, TransactionPruner transactionPruner, MilestoneSolidifier milestoneSolidifier, - BundleValidator bundleValidator, Tangle tangle, TransactionValidator transactionValidator, - TransactionRequester transactionRequester, NeighborRouter neighborRouter, - TransactionProcessingPipeline transactionProcessingPipeline, TipsRequester tipsRequester, - TipsViewModel tipsViewModel, TipSelector tipsSelector, LocalSnapshotsPersistenceProvider localSnapshotsDb, - CacheManager cacheManager) { + SpentAddressesService spentAddressesService, SnapshotProvider snapshotProvider, + SnapshotService snapshotService, LocalSnapshotManager localSnapshotManager, + MilestoneService milestoneService, LatestMilestoneTracker latestMilestoneTracker, + LatestSolidMilestoneTracker latestSolidMilestoneTracker, SeenMilestonesRetriever seenMilestonesRetriever, + LedgerService ledgerService, TransactionPruner transactionPruner, MilestoneSolidifier milestoneSolidifier, + BundleValidator bundleValidator, Tangle tangle, TransactionValidator transactionValidator, + TransactionRequester transactionRequester, NeighborRouter neighborRouter, + TransactionProcessingPipeline transactionProcessingPipeline, TipsRequester tipsRequester, + TipsViewModel tipsViewModel, TipSelector tipsSelector, LocalSnapshotsPersistenceProvider localSnapshotsDb, + CacheManager cacheManager, TransactionSolidifier transactionSolidifier) { this.configuration = configuration; this.ledgerService = ledgerService; @@ -145,9 +148,9 @@ public Iota(IotaConfig configuration, SpentAddressesProvider spentAddressesProvi this.neighborRouter = neighborRouter; this.txPipeline = transactionProcessingPipeline; this.tipsRequester = tipsRequester; + this.transactionSolidifier = transactionSolidifier; this.localSnapshotsDb = localSnapshotsDb; - // legacy classes this.bundleValidator = bundleValidator; this.tangle = tangle; @@ -200,8 +203,6 @@ public void init() throws Exception { tangle.clearMetadata(com.iota.iri.model.persistables.Transaction.class); } - transactionValidator.init(); - txPipeline.start(); neighborRouter.start(); tipsRequester.start(); @@ -210,6 +211,7 @@ public void init() throws Exception { latestSolidMilestoneTracker.start(); seenMilestonesRetriever.start(); milestoneSolidifier.start(); + transactionSolidifier.start(); if (localSnapshotManager != null) { localSnapshotManager.addSnapshotCondition(new SnapshotDepthCondition(configuration, snapshotProvider)); @@ -256,6 +258,7 @@ private void rescanDb() throws Exception { public void shutdown() throws Exception { // shutdown in reverse starting order (to not break any dependencies) milestoneSolidifier.shutdown(); + transactionSolidifier.shutdown(); seenMilestonesRetriever.shutdown(); latestSolidMilestoneTracker.shutdown(); latestMilestoneTracker.shutdown(); @@ -270,7 +273,6 @@ public void shutdown() throws Exception { tipsRequester.shutdown(); txPipeline.shutdown(); neighborRouter.shutdown(); - transactionValidator.shutdown(); localSnapshotsDb.shutdown(); tangle.shutdown(); @@ -314,10 +316,10 @@ private void initializeTangle() { * @return A new Persistance provider */ private PersistenceProvider createRocksDbProvider(String path, String log, String configFile, int cacheSize, - Map> columnFamily, - Map.Entry> metadata) { + Map> columnFamily, + Map.Entry> metadata) { return new RocksDBPersistenceProvider( path, log, configFile, cacheSize, columnFamily, metadata); } -} +} \ No newline at end of file diff --git a/src/main/java/com/iota/iri/MainInjectionConfiguration.java b/src/main/java/com/iota/iri/MainInjectionConfiguration.java index f3963bc391..ed464ea40e 100644 --- a/src/main/java/com/iota/iri/MainInjectionConfiguration.java +++ b/src/main/java/com/iota/iri/MainInjectionConfiguration.java @@ -27,7 +27,9 @@ import com.iota.iri.service.tipselection.impl.*; import com.iota.iri.service.transactionpruning.TransactionPruner; import com.iota.iri.service.transactionpruning.async.AsyncTransactionPruner; +import com.iota.iri.service.validation.TransactionSolidifier; import com.iota.iri.service.validation.TransactionValidator; +import com.iota.iri.service.validation.impl.TransactionSolidifierImpl; import com.iota.iri.storage.LocalSnapshotsPersistenceProvider; import com.iota.iri.storage.Tangle; import com.iota.iri.storage.rocksDB.RocksDBPersistenceProvider; @@ -101,8 +103,8 @@ LatestMilestoneTracker provideLatestMilestoneTracker(Tangle tangle, SnapshotProv @Singleton @Provides LatestSolidMilestoneTracker provideLatestSolidMilestoneTracker(Tangle tangle, SnapshotProvider snapshotProvider, - MilestoneService milestoneService, LedgerService ledgerService, - LatestMilestoneTracker latestMilestoneTracker, TransactionRequester transactionRequester) { + MilestoneService milestoneService, LedgerService ledgerService, + LatestMilestoneTracker latestMilestoneTracker, TransactionRequester transactionRequester) { return new LatestSolidMilestoneTrackerImpl(tangle, snapshotProvider, milestoneService, ledgerService, latestMilestoneTracker, transactionRequester, configuration); } @@ -115,8 +117,8 @@ SeenMilestonesRetriever provideSeenMilestonesRetriever(Tangle tangle, SnapshotPr @Singleton @Provides - MilestoneSolidifier provideMilestoneSolidifier(SnapshotProvider snapshotProvider, TransactionValidator transactionValidator) { - return new MilestoneSolidifierImpl(snapshotProvider, transactionValidator); + MilestoneSolidifier provideMilestoneSolidifier(SnapshotProvider snapshotProvider, TransactionSolidifier transactionSolidifier) { + return new MilestoneSolidifierImpl(snapshotProvider, transactionSolidifier); } @Singleton @@ -137,8 +139,14 @@ LocalSnapshotManager provideLocalSnapshotManager(SnapshotProvider snapshotProvid @Singleton @Provides - TransactionValidator provideTransactionValidator(Tangle tangle, SnapshotProvider snapshotProvider, TipsViewModel tipsViewModel, TransactionRequester transactionRequester) { - return new TransactionValidator(tangle, snapshotProvider, tipsViewModel, transactionRequester, configuration); + TransactionValidator provideTransactionValidator(SnapshotProvider snapshotProvider, TransactionRequester transactionRequester) { + return new TransactionValidator(snapshotProvider, transactionRequester, configuration); + } + + @Singleton + @Provides + TransactionSolidifier provideTransactionSolidifier(Tangle tangle, SnapshotProvider snapshotProvider, TransactionRequester transactionRequester, TipsViewModel tipsViewModel){ + return new TransactionSolidifierImpl(tangle, snapshotProvider, transactionRequester, tipsViewModel); } @Singleton @@ -163,21 +171,21 @@ TipSelector provideTipSelector(Tangle tangle, SnapshotProvider snapshotProvider, @Singleton @Provides Iota provideIota(SpentAddressesProvider spentAddressesProvider, SpentAddressesService spentAddressesService, - SnapshotProvider snapshotProvider, SnapshotService snapshotService, - @Nullable LocalSnapshotManager localSnapshotManager, MilestoneService milestoneService, - LatestMilestoneTracker latestMilestoneTracker, LatestSolidMilestoneTracker latestSolidMilestoneTracker, - SeenMilestonesRetriever seenMilestonesRetriever, LedgerService ledgerService, - @Nullable TransactionPruner transactionPruner, MilestoneSolidifier milestoneSolidifier, - BundleValidator bundleValidator, Tangle tangle, TransactionValidator transactionValidator, - TransactionRequester transactionRequester, NeighborRouter neighborRouter, - TransactionProcessingPipeline transactionProcessingPipeline, TipsRequester tipsRequester, - TipsViewModel tipsViewModel, TipSelector tipsSelector, LocalSnapshotsPersistenceProvider localSnapshotsDb, - CacheManager cacheManager) { + SnapshotProvider snapshotProvider, SnapshotService snapshotService, + @Nullable LocalSnapshotManager localSnapshotManager, MilestoneService milestoneService, + LatestMilestoneTracker latestMilestoneTracker, LatestSolidMilestoneTracker latestSolidMilestoneTracker, + SeenMilestonesRetriever seenMilestonesRetriever, LedgerService ledgerService, + @Nullable TransactionPruner transactionPruner, MilestoneSolidifier milestoneSolidifier, + BundleValidator bundleValidator, Tangle tangle, TransactionValidator transactionValidator, + TransactionRequester transactionRequester, NeighborRouter neighborRouter, + TransactionProcessingPipeline transactionProcessingPipeline, TipsRequester tipsRequester, + TipsViewModel tipsViewModel, TipSelector tipsSelector, LocalSnapshotsPersistenceProvider localSnapshotsDb, + CacheManager cacheManager, TransactionSolidifier transactionSolidifier) { return new Iota(configuration, spentAddressesProvider, spentAddressesService, snapshotProvider, snapshotService, localSnapshotManager, milestoneService, latestMilestoneTracker, latestSolidMilestoneTracker, seenMilestonesRetriever, ledgerService, transactionPruner, milestoneSolidifier, bundleValidator, tangle, transactionValidator, transactionRequester, neighborRouter, transactionProcessingPipeline, - tipsRequester, tipsViewModel, tipsSelector, localSnapshotsDb, cacheManager); + tipsRequester, tipsViewModel, tipsSelector, localSnapshotsDb, cacheManager, transactionSolidifier); } @Singleton @@ -189,11 +197,11 @@ IXI provideIxi(Iota iota) { @Singleton @Provides API provideApi(IXI ixi, TransactionRequester transactionRequester, - SpentAddressesService spentAddressesService, Tangle tangle, BundleValidator bundleValidator, - SnapshotProvider snapshotProvider, LedgerService ledgerService, NeighborRouter neighborRouter, TipSelector tipsSelector, - TipsViewModel tipsViewModel, TransactionValidator transactionValidator, - LatestMilestoneTracker latestMilestoneTracker, TransactionProcessingPipeline txPipeline) { - return new API(configuration, ixi, transactionRequester, spentAddressesService, tangle, bundleValidator, snapshotProvider, ledgerService, neighborRouter, tipsSelector, tipsViewModel, transactionValidator, latestMilestoneTracker, txPipeline); + SpentAddressesService spentAddressesService, Tangle tangle, BundleValidator bundleValidator, + SnapshotProvider snapshotProvider, LedgerService ledgerService, NeighborRouter neighborRouter, TipSelector tipsSelector, + TipsViewModel tipsViewModel, TransactionValidator transactionValidator, + LatestMilestoneTracker latestMilestoneTracker, TransactionProcessingPipeline txPipeline, TransactionSolidifier transactionSolidifier) { + return new API(configuration, ixi, transactionRequester, spentAddressesService, tangle, bundleValidator, snapshotProvider, ledgerService, neighborRouter, tipsSelector, tipsViewModel, transactionValidator, latestMilestoneTracker, txPipeline, transactionSolidifier); } @Singleton @@ -214,4 +222,4 @@ protected void configure() { bind(BundleValidator.class).asEagerSingleton(); bind(TipsViewModel.class).asEagerSingleton(); } -} +} \ No newline at end of file diff --git a/src/main/java/com/iota/iri/controllers/TransactionViewModel.java b/src/main/java/com/iota/iri/controllers/TransactionViewModel.java index 207d7cb72d..0beb399048 100644 --- a/src/main/java/com/iota/iri/controllers/TransactionViewModel.java +++ b/src/main/java/com/iota/iri/controllers/TransactionViewModel.java @@ -779,28 +779,6 @@ public void setMetadata() { : TransactionViewModel.FILLED_SLOT; } - /** - * Update solid transactions - * @param tangle Tangle - * @param initialSnapshot Initial snapshot - * @param analyzedHashes analyzed hashes - * @throws Exception Exception - */ - public static void updateSolidTransactions(Tangle tangle, Snapshot initialSnapshot, - final Set analyzedHashes) throws Exception { - Object[] hashes = analyzedHashes.toArray(); - TransactionViewModel transactionViewModel; - for (int i = hashes.length - 1; i >= 0; i--) { - transactionViewModel = TransactionViewModel.fromHash(tangle, (Hash) hashes[i]); - - transactionViewModel.updateHeights(tangle, initialSnapshot); - - if (!transactionViewModel.isSolid()) { - transactionViewModel.updateSolid(true); - transactionViewModel.update(tangle, initialSnapshot, "solid|height"); - } - } - } /** * Updates the {@link Transaction#solid} value of the referenced {@link Transaction} object. diff --git a/src/main/java/com/iota/iri/network/NetworkInjectionConfiguration.java b/src/main/java/com/iota/iri/network/NetworkInjectionConfiguration.java index 0bda0a6520..5d0f82d839 100644 --- a/src/main/java/com/iota/iri/network/NetworkInjectionConfiguration.java +++ b/src/main/java/com/iota/iri/network/NetworkInjectionConfiguration.java @@ -3,6 +3,7 @@ import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.Singleton; +import com.iota.iri.service.validation.TransactionSolidifier; import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.conf.IotaConfig; import com.iota.iri.controllers.TipsViewModel; @@ -44,11 +45,11 @@ TipsRequester provideTipsRequester(NeighborRouter neighborRouter, Tangle tangle, @Singleton @Provides TransactionProcessingPipeline provideTransactionProcessingPipeline(NeighborRouter neighborRouter, - TransactionValidator txValidator, Tangle tangle, SnapshotProvider snapshotProvider, - TipsViewModel tipsViewModel, LatestMilestoneTracker latestMilestoneTracker, - TransactionRequester transactionRequester) { + TransactionValidator txValidator, Tangle tangle, SnapshotProvider snapshotProvider, + TipsViewModel tipsViewModel, LatestMilestoneTracker latestMilestoneTracker, + TransactionRequester transactionRequester, TransactionSolidifier transactionSolidifier) { return new TransactionProcessingPipelineImpl(neighborRouter, configuration, txValidator, tangle, - snapshotProvider, tipsViewModel, latestMilestoneTracker, transactionRequester); + snapshotProvider, tipsViewModel, latestMilestoneTracker, transactionRequester, transactionSolidifier); } @Singleton @@ -57,4 +58,4 @@ NeighborRouter provideNeighborRouter(TransactionRequester transactionRequester, return new NeighborRouterImpl(configuration, configuration, transactionRequester, transactionProcessingPipeline); } -} +} \ No newline at end of file diff --git a/src/main/java/com/iota/iri/network/pipeline/ReceivedStage.java b/src/main/java/com/iota/iri/network/pipeline/ReceivedStage.java index 2f07ce7b75..9ac482b30e 100644 --- a/src/main/java/com/iota/iri/network/pipeline/ReceivedStage.java +++ b/src/main/java/com/iota/iri/network/pipeline/ReceivedStage.java @@ -1,6 +1,6 @@ package com.iota.iri.network.pipeline; -import com.iota.iri.service.validation.TransactionValidator; +import com.iota.iri.service.validation.TransactionSolidifier; import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.network.TransactionRequester; import com.iota.iri.network.neighbor.Neighbor; @@ -19,19 +19,19 @@ public class ReceivedStage implements Stage { private Tangle tangle; private TransactionRequester transactionRequester; - private TransactionValidator txValidator; + private TransactionSolidifier txSolidifier; private SnapshotProvider snapshotProvider; /** * Creates a new {@link ReceivedStage}. - * + * * @param tangle The {@link Tangle} database used to store/update the transaction - * @param txValidator The {@link TransactionValidator} used to store/update the transaction + * @param txSolidifier The {@link TransactionSolidifier} used to store/update the transaction * @param snapshotProvider The {@link SnapshotProvider} used to store/update the transaction */ - public ReceivedStage(Tangle tangle, TransactionValidator txValidator, SnapshotProvider snapshotProvider, + public ReceivedStage(Tangle tangle, TransactionSolidifier txSolidifier, SnapshotProvider snapshotProvider, TransactionRequester transactionRequester) { - this.txValidator = txValidator; + this.txSolidifier = txSolidifier; this.tangle = tangle; this.snapshotProvider = snapshotProvider; this.transactionRequester = transactionRequester; @@ -39,8 +39,8 @@ public ReceivedStage(Tangle tangle, TransactionValidator txValidator, SnapshotPr /** * Stores the given transaction in the database, updates it status - * ({@link TransactionValidator#updateStatus(TransactionViewModel)}) and updates the sender. - * + * ({@link TransactionSolidifier#updateStatus(TransactionViewModel)}) and updates the sender. + * * @param ctx the received stage {@link ProcessingContext} * @return a {@link ProcessingContext} which redirects to the {@link BroadcastStage} */ @@ -65,7 +65,7 @@ public ProcessingContext process(ProcessingContext ctx) { if (stored) { tvm.setArrivalTime(System.currentTimeMillis()); try { - txValidator.updateStatus(tvm); + txSolidifier.updateStatus(tvm); // free up the recently requested transaction set if(transactionRequester.removeRecentlyRequestedTransaction(tvm.getHash())){ @@ -91,8 +91,8 @@ public ProcessingContext process(ProcessingContext ctx) { } // broadcast the newly saved tx to the other neighbors - ctx.setNextStage(TransactionProcessingPipeline.Stage.BROADCAST); - ctx.setPayload(new BroadcastPayload(originNeighbor, tvm)); + ctx.setNextStage(TransactionProcessingPipeline.Stage.SOLIDIFY); + ctx.setPayload(new SolidifyPayload(originNeighbor, tvm)); return ctx; } -} +} \ No newline at end of file diff --git a/src/main/java/com/iota/iri/network/pipeline/SolidifyPayload.java b/src/main/java/com/iota/iri/network/pipeline/SolidifyPayload.java new file mode 100644 index 0000000000..6bd84029ef --- /dev/null +++ b/src/main/java/com/iota/iri/network/pipeline/SolidifyPayload.java @@ -0,0 +1,39 @@ +package com.iota.iri.network.pipeline; + +import com.iota.iri.controllers.TransactionViewModel; +import com.iota.iri.network.neighbor.Neighbor; + +/** + * Defines a payload which gets submitted to the {@link SolidifyStage}. + */ +public class SolidifyPayload extends Payload { + private Neighbor originNeighbor; + private TransactionViewModel tvm; + + /** + * Constructor for solidification payload. + * + * @param originNeighbor The originating point of a received transaction + * @param tvm The transaction that needs to be solidified + */ + public SolidifyPayload(Neighbor originNeighbor, TransactionViewModel tvm){ + this.originNeighbor = originNeighbor; + this.tvm = tvm; + } + + /** + * {@inheritDoc} + */ + @Override + public Neighbor getOriginNeighbor(){ + return originNeighbor; + } + + /** + * Fetches the transaction from the payload. + * @return The transaction stored in the payload. + */ + public TransactionViewModel getTransaction(){ + return tvm; + } +} \ No newline at end of file diff --git a/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java b/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java new file mode 100644 index 0000000000..85d4ee85ac --- /dev/null +++ b/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java @@ -0,0 +1,97 @@ +package com.iota.iri.network.pipeline; + +import com.google.common.annotations.VisibleForTesting; +import com.iota.iri.controllers.TipsViewModel; +import com.iota.iri.controllers.TransactionViewModel; +import com.iota.iri.model.Hash; +import com.iota.iri.service.validation.TransactionSolidifier; +import com.iota.iri.storage.Tangle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.iota.iri.controllers.TransactionViewModel.fromHash; + +/** + * The {@link SolidifyStage} is used to process newly received transaction for solidity. Once a transaction has been + * passed from the {@link ReceivedStage} it will be placed into this stage to have the {@link TransactionSolidifier} + * check the solidity of the transaction. If the transaction is found to be solid, it will be passed forward to the + * {@link BroadcastStage}. If it is found to be unsolid, it is put through the solidity check so that missing reference + * transactions get requested. If the transaction is unsolid, a random solid tip is broadcast instead to keep the + * requests transmitting to neighbors. + */ +public class SolidifyStage implements Stage { + private static final Logger log = LoggerFactory.getLogger(SolidifyStage.class); + + private TransactionSolidifier txSolidifier; + private TipsViewModel tipsViewModel; + private Tangle tangle; + private TransactionViewModel tip; + + /** + * Constructor for the {@link SolidifyStage}. + * + * @param txSolidifier Transaction validator implementation for determining the validity of a transaction + * @param tipsViewModel Used for broadcasting random solid tips if the subject transaction is unsolid + * @param tangle A reference to the nodes DB + */ + public SolidifyStage(TransactionSolidifier txSolidifier, TipsViewModel tipsViewModel, Tangle tangle){ + this.txSolidifier = txSolidifier; + this.tipsViewModel = tipsViewModel; + this.tangle = tangle; + } + + /** + * Processes the payload of the {@link ProcessingContext} as a {@link SolidifyPayload}. First the transaction will + * be checked for solidity and validity. If the transaction is already solid or can be set solid quickly by the + * transaction validator, the transaction is passed to the {@link BroadcastStage}. If not, a random solid tip is + * pulled form the {@link TipsViewModel} to be broadcast instead. + * + * @param ctx The context to process + * @return The output context, in most cases a {@link BroadcastPayload}. + */ + @Override + public ProcessingContext process(ProcessingContext ctx){ + try { + SolidifyPayload payload = (SolidifyPayload) ctx.getPayload(); + TransactionViewModel tvm = payload.getTransaction(); + + if (tvm.isSolid() || txSolidifier.quickSetSolid(tvm)) { + ctx.setNextStage(TransactionProcessingPipeline.Stage.BROADCAST); + ctx.setPayload(new BroadcastPayload(payload.getOriginNeighbor(), payload.getTransaction())); + return ctx; + } + + return broadcastTip(ctx, payload); + }catch (Exception e){ + log.error("Failed to process transaction for solidification", e); + ctx.setNextStage(TransactionProcessingPipeline.Stage.ABORT); + return ctx; + } + + } + + private ProcessingContext broadcastTip(ProcessingContext ctx, SolidifyPayload payload) throws Exception{ + if(tip == null) { + Hash tipHash = tipsViewModel.getRandomSolidTipHash(); + + if (tipHash == null) { + ctx.setNextStage(TransactionProcessingPipeline.Stage.FINISH); + return ctx; + } + + tip = fromHash(tangle, tipHash); + } + + ctx.setNextStage(TransactionProcessingPipeline.Stage.BROADCAST); + ctx.setPayload(new BroadcastPayload(payload.getOriginNeighbor(), tip)); + + tip = null; + return ctx; + } + + @VisibleForTesting + void injectTip(TransactionViewModel tvm) throws Exception { + tip = tvm; + tip.updateSolid(true); + } +} diff --git a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipeline.java b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipeline.java index 1f19e4248c..081deb810a 100644 --- a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipeline.java +++ b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipeline.java @@ -1,6 +1,7 @@ package com.iota.iri.network.pipeline; import com.iota.iri.network.neighbor.Neighbor; +import com.iota.iri.service.validation.TransactionSolidifier; import java.nio.ByteBuffer; import java.util.concurrent.BlockingQueue; @@ -14,7 +15,7 @@ public interface TransactionProcessingPipeline { * Defines the different stages of the {@link TransactionProcessingPipelineImpl}. */ enum Stage { - PRE_PROCESS, HASHING, VALIDATION, REPLY, RECEIVED, BROADCAST, MULTIPLE, ABORT, FINISH, + PRE_PROCESS, HASHING, VALIDATION, REPLY, RECEIVED, BROADCAST, MULTIPLE, ABORT, FINISH, SOLIDIFY, } /** @@ -65,6 +66,12 @@ enum Stage { */ void process(byte[] txTrits); + /** + * Fetches a set of transactions from the {@link TransactionSolidifier} and submits + * the object into the {@link BroadcastStage} queue. + */ + void refillBroadcastQueue(); + /** * Shut downs the pipeline by shutting down all stages. */ @@ -111,4 +118,11 @@ enum Stage { * @param hashingStage the {@link HashingStage} to use */ void setHashingStage(HashingStage hashingStage); -} + + /** + * Sets the solidify stage. This method should only be used for injecting mocked objects. + * + * @param solidifyStage the {@link SolidifyStage} to use + */ + void setSolidifyStage(SolidifyStage solidifyStage); +} \ No newline at end of file diff --git a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java index 500fc01611..8f8c57d753 100644 --- a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java +++ b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java @@ -1,8 +1,10 @@ package com.iota.iri.network.pipeline; +import com.iota.iri.service.validation.TransactionSolidifier; import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.conf.NodeConfig; import com.iota.iri.controllers.TipsViewModel; +import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.crypto.batched.BatchedHasher; import com.iota.iri.crypto.batched.BatchedHasherFactory; import com.iota.iri.crypto.batched.HashRequest; @@ -19,7 +21,10 @@ import com.iota.iri.utils.Converter; import java.nio.ByteBuffer; +import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; @@ -69,12 +74,15 @@ public class TransactionProcessingPipelineImpl implements TransactionProcessingP private BroadcastStage broadcastStage; private BatchedHasher batchedHasher; private HashingStage hashingStage; + private SolidifyStage solidifyStage; + private TransactionSolidifier txSolidifier; private BlockingQueue preProcessStageQueue = new ArrayBlockingQueue<>(100); private BlockingQueue validationStageQueue = new ArrayBlockingQueue<>(100); private BlockingQueue receivedStageQueue = new ArrayBlockingQueue<>(100); - private BlockingQueue broadcastStageQueue = new ArrayBlockingQueue<>(100); private BlockingQueue replyStageQueue = new ArrayBlockingQueue<>(100); + private BlockingQueue broadcastStageQueue = new ArrayBlockingQueue<>(100); + private BlockingQueue solidifyStageQueue = new ArrayBlockingQueue<>(100); /** * Creates a {@link TransactionProcessingPipeline}. @@ -89,18 +97,20 @@ public class TransactionProcessingPipelineImpl implements TransactionProcessingP * reply stage */ public TransactionProcessingPipelineImpl(NeighborRouter neighborRouter, NodeConfig config, - TransactionValidator txValidator, Tangle tangle, SnapshotProvider snapshotProvider, - TipsViewModel tipsViewModel, LatestMilestoneTracker latestMilestoneTracker, - TransactionRequester transactionRequester) { + TransactionValidator txValidator, Tangle tangle, SnapshotProvider snapshotProvider, + TipsViewModel tipsViewModel, LatestMilestoneTracker latestMilestoneTracker, + TransactionRequester transactionRequester, TransactionSolidifier txSolidifier) { FIFOCache recentlySeenBytesCache = new FIFOCache<>(config.getCacheSizeBytes()); this.preProcessStage = new PreProcessStage(recentlySeenBytesCache); this.replyStage = new ReplyStage(neighborRouter, config, tangle, tipsViewModel, latestMilestoneTracker, snapshotProvider, recentlySeenBytesCache); this.broadcastStage = new BroadcastStage(neighborRouter); this.validationStage = new ValidationStage(txValidator, recentlySeenBytesCache); - this.receivedStage = new ReceivedStage(tangle, txValidator, snapshotProvider, transactionRequester); + this.receivedStage = new ReceivedStage(tangle, txSolidifier, snapshotProvider, transactionRequester); this.batchedHasher = BatchedHasherFactory.create(BatchedHasherFactory.Type.BCTCURL81, 20); this.hashingStage = new HashingStage(batchedHasher); + this.solidifyStage = new SolidifyStage(txSolidifier, tipsViewModel, tangle); + this.txSolidifier = txSolidifier; } @Override @@ -111,21 +121,23 @@ public void start() { addStage("reply", replyStageQueue, replyStage); addStage("received", receivedStageQueue, receivedStage); addStage("broadcast", broadcastStageQueue, broadcastStage); + addStage("solidify", solidifyStageQueue, solidifyStage); } /** * Adds the given stage to the processing pipeline. - * + * * @param name the name of the stage * @param queue the queue from which contexts are taken to process within the stage * @param stage the stage with the processing logic */ private void addStage(String name, BlockingQueue queue, - com.iota.iri.network.pipeline.Stage stage) { + com.iota.iri.network.pipeline.Stage stage) { stagesThreadPool.submit(new Thread(() -> { try { while (!Thread.currentThread().isInterrupted()) { ProcessingContext ctx = stage.process(queue.take()); + switch (ctx.getNextStage()) { case REPLY: replyStageQueue.put(ctx); @@ -144,6 +156,9 @@ private void addStage(String name, BlockingQueue queue, case BROADCAST: broadcastStageQueue.put(ctx); break; + case SOLIDIFY: + solidifyStageQueue.put(ctx); + break; case ABORT: break; case FINISH: @@ -184,6 +199,7 @@ public BlockingQueue getValidationStageQueue() { public void process(Neighbor neighbor, ByteBuffer data) { try { preProcessStageQueue.put(new ProcessingContext(new PreProcessPayload(neighbor, data))); + refillBroadcastQueue(); } catch (InterruptedException e) { e.printStackTrace(); } @@ -198,10 +214,27 @@ public void process(byte[] txTrits) { hashAndValidate(new ProcessingContext(payload)); } + @Override + public void refillBroadcastQueue(){ + try{ + Iterator hashIterator = txSolidifier.getBroadcastQueue().iterator(); + Set toRemove = new LinkedHashSet<>(); + while(!Thread.currentThread().isInterrupted() && hashIterator.hasNext()){ + TransactionViewModel tx = hashIterator.next(); + broadcastStageQueue.put(new ProcessingContext(new BroadcastPayload(null, tx))); + toRemove.add(tx); + hashIterator.remove(); + } + txSolidifier.clearFromBroadcastQueue(toRemove); + } catch(InterruptedException e){ + log.info(e.getMessage()); + } + } + /** * Sets up the given hashing stage {@link ProcessingContext} so that up on success, it will submit further to the * validation stage. - * + * * @param ctx the hashing stage {@link ProcessingContext} */ private void hashAndValidate(ProcessingContext ctx) { @@ -255,4 +288,9 @@ public void setBroadcastStage(BroadcastStage broadcastStage) { public void setHashingStage(HashingStage hashingStage) { this.hashingStage = hashingStage; } -} + + @Override + public void setSolidifyStage(SolidifyStage solidifyStage){ + this.solidifyStage = solidifyStage; + } +} \ No newline at end of file diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java index d90376c933..1471cd990d 100644 --- a/src/main/java/com/iota/iri/service/API.java +++ b/src/main/java/com/iota/iri/service/API.java @@ -7,6 +7,7 @@ import com.iota.iri.IRI; import com.iota.iri.IXI; import com.iota.iri.service.validation.TransactionValidator; +import com.iota.iri.service.validation.TransactionSolidifier; import com.iota.iri.conf.APIConfig; import com.iota.iri.conf.IotaConfig; import com.iota.iri.controllers.*; @@ -66,33 +67,33 @@ public class API { private static final Logger log = LoggerFactory.getLogger(API.class); - + //region [CONSTANTS] /////////////////////////////////////////////////////////////////////////////// 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."; - + + "The subtangle has not been updated yet."; + private static final String OVER_MAX_ERROR_MESSAGE = "Could not complete request"; private static final String INVALID_PARAMS = "Invalid parameters"; private static final char ZERO_LENGTH_ALLOWED = 'Y'; private static final char ZERO_LENGTH_NOT_ALLOWED = 'N'; - + private static final int HASH_SIZE = 81; private static final int TRYTES_SIZE = 2673; private static final long MAX_TIMESTAMP_VALUE = (long) (Math.pow(3, 27) - 1) / 2; // max positive 27-trits value //endregion //////////////////////////////////////////////////////////////////////////////////////////////////////// - + private static int counterGetTxToApprove = 0; private static long ellapsedTime_getTxToApprove = 0L; private static int counter_PoW = 0; private static long ellapsedTime_PoW = 0L; - + //region [CONSTRUCTOR_FIELDS] /////////////////////////////////////////////////////////////////////////////// private final IotaConfig configuration; @@ -108,14 +109,15 @@ public class API { private final TipSelector tipsSelector; private final TipsViewModel tipsViewModel; private final TransactionValidator transactionValidator; + private final TransactionSolidifier transactionSolidifier; private final LatestMilestoneTracker latestMilestoneTracker; - + private final int maxFindTxs; private final int maxRequestList; private final int maxGetTrytes; private final String[] features; - + //endregion //////////////////////////////////////////////////////////////////////////////////////////////////////// private final Gson gson = new GsonBuilder().create(); @@ -134,9 +136,9 @@ public class API { /** * Starts loading the IOTA API, parameters do not have to be initialized. - * + * * @param configuration Holds IRI configuration parameters. - * @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} * @param transactionRequester Service where transactions get requested * @param spentAddressesService Service to check if addresses are spent @@ -149,15 +151,19 @@ public class API { * @param tipsViewModel Contains the current tips of this node * @param transactionValidator Validates transactions * @param latestMilestoneTracker Service that tracks the latest milestone + * @param transactionSolidifier Holds transaction pipeline, including broadcast transactions + * @param t Service that tracks the latest milestone + * */ public API(IotaConfig configuration, IXI ixi, TransactionRequester transactionRequester, - SpentAddressesService spentAddressesService, Tangle tangle, BundleValidator bundleValidator, - SnapshotProvider snapshotProvider, LedgerService ledgerService, NeighborRouter neighborRouter, TipSelector tipsSelector, - TipsViewModel tipsViewModel, TransactionValidator transactionValidator, - LatestMilestoneTracker latestMilestoneTracker, TransactionProcessingPipeline txPipeline) { + SpentAddressesService spentAddressesService, Tangle tangle, BundleValidator bundleValidator, + SnapshotProvider snapshotProvider, LedgerService ledgerService, NeighborRouter neighborRouter, + TipSelector tipsSelector, TipsViewModel tipsViewModel, TransactionValidator transactionValidator, + LatestMilestoneTracker latestMilestoneTracker, TransactionProcessingPipeline txPipeline, + TransactionSolidifier transactionSolidifier) { this.configuration = configuration; this.ixi = ixi; - + this.transactionRequester = transactionRequester; this.spentAddressesService = spentAddressesService; this.tangle = tangle; @@ -169,14 +175,15 @@ public API(IotaConfig configuration, IXI ixi, TransactionRequester transactionRe this.tipsSelector = tipsSelector; this.tipsViewModel = tipsViewModel; this.transactionValidator = transactionValidator; + this.transactionSolidifier = transactionSolidifier; this.latestMilestoneTracker = latestMilestoneTracker; - + maxFindTxs = configuration.getMaxFindTransactions(); maxRequestList = configuration.getMaxRequestsList(); maxGetTrytes = configuration.getMaxGetTrytes(); features = Feature.calculateFeatureNames(configuration); - + commandRoute = new HashMap<>(); commandRoute.put(ApiCommand.ADD_NEIGHBORS, addNeighbors()); commandRoute.put(ApiCommand.ATTACH_TO_TANGLE, attachToTangle()); @@ -201,7 +208,7 @@ public API(IotaConfig configuration, IXI ixi, TransactionRequester transactionRe /** * Initializes the API for usage. * Will initialize and start the supplied {@link RestConnector} - * + * * @param connector THe connector we use to handle API requests */ public void init(RestConnector connector){ @@ -209,7 +216,7 @@ public void init(RestConnector connector){ connector.init(this::process); connector.start(); } - + /** * Handles an API request body. * Its returned {@link AbstractResponse} is created using the following logic @@ -472,20 +479,20 @@ private AbstractResponse addNeighborsStatement(List uris) { } return AddedNeighborsResponse.create(numberOfAddedNeighbors); } - + /** - * Temporarily removes a list of neighbors from your node. - * The added neighbors will be added again after relaunching IRI. - * Remove the neighbors from your config file or make sure you don't supply them in the -n command line option if you want to keep them removed after restart. - * - * The URI (Unique Resource Identification) for removing neighbors is: - * udp://IPADDRESS:PORT - * - * Returns an {@link com.iota.iri.service.dto.ErrorResponse} if the URI scheme is wrong - * - * @param uris The URIs of the neighbors we want to remove. - * @return {@link com.iota.iri.service.dto.RemoveNeighborsResponse} - **/ + * Temporarily removes a list of neighbors from your node. + * The added neighbors will be added again after relaunching IRI. + * Remove the neighbors from your config file or make sure you don't supply them in the -n command line option if you want to keep them removed after restart. + * + * The URI (Unique Resource Identification) for removing neighbors is: + * udp://IPADDRESS:PORT + * + * Returns an {@link com.iota.iri.service.dto.ErrorResponse} if the URI scheme is wrong + * + * @param uris The URIs of the neighbors we want to remove. + * @return {@link com.iota.iri.service.dto.RemoveNeighborsResponse} + **/ @Document(name="removeNeighbors") private AbstractResponse removeNeighborsStatement(List uris) { int numberOfRemovedNeighbors = 0; @@ -514,13 +521,13 @@ private AbstractResponse removeNeighborsStatement(List uris) { } /** - * Returns raw transaction data (trytes) of a specific transaction. - * These trytes can then be converted into the actual transaction object. - * See utility and {@link Transaction} functions in an IOTA library for more details. - * - * @param hashes The transaction hashes you want to get trytes from. - * @return {@link com.iota.iri.service.dto.GetTrytesResponse} - **/ + * Returns raw transaction data (trytes) of a specific transaction. + * These trytes can then be converted into the actual transaction object. + * See utility and {@link Transaction} functions in an IOTA library for more details. + * + * @param hashes The transaction hashes you want to get trytes from. + * @return {@link com.iota.iri.service.dto.GetTrytesResponse} + **/ @Document(name="getTrytes") private synchronized AbstractResponse getTrytesStatement(List hashes) throws Exception { final List elements = new LinkedList<>(); @@ -572,17 +579,17 @@ private static void incEllapsedTimeGetTxToApprove(long ellapsedTime) { } /** - * Tip selection which returns trunkTransaction and branchTransaction. - * The input value depth determines how many milestones to go back for finding the transactions to approve. - * The higher your depth value, the more work you have to do as you are confirming more transactions. - * If the depth is too large (usually above 15, it depends on the node's configuration) an error will be returned. - * The reference is an optional hash of a transaction you want to approve. - * If it can't be found at the specified depth then an error will be returned. - * - * @param depth Number of bundles to go back to determine the transactions for approval. - * @param reference Hash of transaction to start random-walk from, used to make sure the tips returned reference a given transaction in their past. - * @return {@link com.iota.iri.service.dto.GetTransactionsToApproveResponse} - **/ + * Tip selection which returns trunkTransaction and branchTransaction. + * The input value depth determines how many milestones to go back for finding the transactions to approve. + * The higher your depth value, the more work you have to do as you are confirming more transactions. + * If the depth is too large (usually above 15, it depends on the node's configuration) an error will be returned. + * The reference is an optional hash of a transaction you want to approve. + * If it can't be found at the specified depth then an error will be returned. + * + * @param depth Number of bundles to go back to determine the transactions for approval. + * @param reference Hash of transaction to start random-walk from, used to make sure the tips returned reference a given transaction in their past. + * @return {@link com.iota.iri.service.dto.GetTransactionsToApproveResponse} + **/ @Document(name="getTransactionsToApprove") private synchronized AbstractResponse getTransactionsToApproveStatement(int depth, Optional reference) { if (depth < 0 || depth > configuration.getMaxDepth()) { @@ -634,7 +641,7 @@ List getTransactionToApproveTips(int depth, Optional reference) thro /** *

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

*

@@ -655,10 +662,10 @@ private void gatherStatisticsOnTipSelection() { } /** - * Returns all tips currently known by this node. - * - * @return {@link com.iota.iri.service.dto.GetTipsResponse} - **/ + * Returns all tips currently known by this node. + * + * @return {@link com.iota.iri.service.dto.GetTipsResponse} + **/ @Document(name="getTips") private synchronized AbstractResponse getTipsStatement() throws Exception { return GetTipsResponse.create(tipsViewModel.getTips() @@ -668,14 +675,14 @@ private synchronized AbstractResponse getTipsStatement() throws Exception { } /** - * Stores transactions in the local storage. - * 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 Transaction data to be stored. - * @return {@link com.iota.iri.service.dto.AbstractResponse.Emptyness} - * @throws Exception When storing or updating a transaction fails. - **/ + * Stores transactions in the local storage. + * 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 Transaction data to be stored. + * @return {@link com.iota.iri.service.dto.AbstractResponse.Emptyness} + * @throws Exception When storing or updating a transaction fails. + **/ @Document(name="storeTransactions") public AbstractResponse storeTransactionsStatement(List trytes) throws Exception { final List elements = convertTrytes(trytes); @@ -683,7 +690,7 @@ public AbstractResponse storeTransactionsStatement(List trytes) throws E //store transactions if(transactionViewModel.store(tangle, snapshotProvider.getInitialSnapshot())) { transactionViewModel.setArrivalTime(System.currentTimeMillis()); - transactionValidator.updateStatus(transactionViewModel); + transactionSolidifier.updateStatus(transactionViewModel); transactionViewModel.updateSender("local"); transactionViewModel.update(tangle, snapshotProvider.getInitialSnapshot(), "sender"); } @@ -692,10 +699,10 @@ public AbstractResponse storeTransactionsStatement(List trytes) throws E } /** - * Interrupts and completely aborts the attachToTangle process. - * - * @return {@link com.iota.iri.service.dto.AbstractResponse.Emptyness} - **/ + * Interrupts and completely aborts the attachToTangle process. + * + * @return {@link com.iota.iri.service.dto.AbstractResponse.Emptyness} + **/ @Document(name="interruptAttachingToTangle") private AbstractResponse interruptAttachingToTangleStatement(){ pearlDiver.cancel(); @@ -703,16 +710,16 @@ private AbstractResponse interruptAttachingToTangleStatement(){ } /** - * Returns information about this node. - * - * @return {@link com.iota.iri.service.dto.GetNodeInfoResponse} - * @throws Exception When we cant find the first milestone in the database - **/ + * Returns information about this node. + * + * @return {@link com.iota.iri.service.dto.GetNodeInfoResponse} + * @throws Exception When we cant find the first milestone in the database + **/ @Document(name="getNodeInfo") private AbstractResponse getNodeInfoStatement() throws Exception{ String name = configuration.isTestnet() ? IRI.TESTNET_NAME : IRI.MAINNET_NAME; MilestoneViewModel milestone = MilestoneViewModel.first(tangle); - + return GetNodeInfoResponse.create( name, IotaUtils.getIriVersion(), @@ -724,13 +731,13 @@ private AbstractResponse getNodeInfoStatement() throws Exception{ Runtime.getRuntime().totalMemory(), latestMilestoneTracker.getLatestMilestoneHash(), latestMilestoneTracker.getLatestMilestoneIndex(), - + snapshotProvider.getLatestSnapshot().getHash(), snapshotProvider.getLatestSnapshot().getIndex(), - + milestone != null ? milestone.index() : -1, snapshotProvider.getLatestSnapshot().getInitialIndex(), - + neighborRouter.getConnectedNeighbors().size(), txPipeline.getBroadcastStageQueue().size(), System.currentTimeMillis(), @@ -842,20 +849,20 @@ private boolean exhaustiveSearchWithinIndex( } /** - *

- * Find transactions that contain the given values in their transaction fields. - * 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 transaction fields returns transactions hashes at the intersection of those values. - * - * @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 - * or the total transactions to find exceeds {@link APIConfig#getMaxFindTransactions()}. - **/ + *

+ * Find transactions that contain the given values in their transaction fields. + * 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 transaction fields returns transactions hashes at the intersection of those values. + * + * @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 + * or the total transactions to find exceeds {@link APIConfig#getMaxFindTransactions()}. + **/ @Document(name="findTransactions") private synchronized AbstractResponse findTransactionsStatement(final Map request) throws Exception { @@ -868,7 +875,7 @@ private synchronized AbstractResponse findTransactionsStatement(final Maptag is a {@link Hash#NULL_HASH}. */ private String padTag(String tag) throws ValidationException { @@ -992,13 +999,13 @@ 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 - * @return {@link com.iota.iri.service.dto.AbstractResponse.Emptyness} - **/ + * 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 + * @return {@link com.iota.iri.service.dto.AbstractResponse.Emptyness} + **/ @Document(name="broadcastTransactions") public AbstractResponse broadcastTransactionsStatement(List trytes) { for (final String tryte : trytes) { @@ -1011,23 +1018,23 @@ public AbstractResponse broadcastTransactionsStatement(List trytes) { /** - *

- * Calculates the confirmed balance, as viewed by the specified tips. - * If the tips parameter is missing, the returned balance is correct as of the latest confirmed 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. - *

- * - * @param addresses Address for which to get the balance (do not include the checksum) - * @param tips The optional tips to find the balance through. - * @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 - **/ + *

+ * Calculates the confirmed balance, as viewed by the specified tips. + * If the tips parameter is missing, the returned balance is correct as of the latest confirmed 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. + *

+ * + * @param addresses Address for which to get the balance (do not include the checksum) + * @param tips The optional tips to find the balance through. + * @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 + **/ @Document(name="getBalances") - private AbstractResponse getBalancesStatement(List addresses, + private AbstractResponse getBalancesStatement(List addresses, List tips, int threshold) throws Exception { @@ -1129,37 +1136,37 @@ 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 obtainable by the getTransactionsToApprove API call. - *

- *

- * 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: - *

    - *
  • trunkTransaction
  • - *
  • branchTransaction
  • - *
  • nonce
  • - *
- *

- * These are valid trytes which are then accepted by the network. - * @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. - * 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. - * @return The list of transactions in trytes, ready to be broadcast to the network. - **/ + *

+ * 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 obtainable by the getTransactionsToApprove API call. + *

+ *

+ * 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: + *

    + *
  • trunkTransaction
  • + *
  • branchTransaction
  • + *
  • nonce
  • + *
+ *

+ * These are valid trytes which are then accepted by the network. + * @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. + * 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. + * @return The list of transactions in trytes, ready to be broadcast to the network. + **/ @Document(name="attachToTangle", returnParam="trytes") public synchronized List attachToTangleStatement(Hash trunkTransaction, Hash branchTransaction, int minWeightMagnitude, List trytes) { @@ -1440,7 +1447,7 @@ private synchronized AbstractResponse storeMessageStatement(String address, Stri return AbstractResponse.createEmptyResponse(); } - + // // FUNCTIONAL COMMAND ROUTES // @@ -1487,10 +1494,10 @@ private Function, AbstractResponse> getBalances() { return request -> { final List addresses = getParameterAsList(request,"addresses", HASH_SIZE); final List tips = request.containsKey("tips") ? - getParameterAsList(request,"tips", HASH_SIZE): - null; + getParameterAsList(request,"tips", HASH_SIZE): + null; final int threshold = getParameterAsInt(request, "threshold"); - + try { return getBalancesStatement(addresses, tips, threshold); } catch (Exception e) { @@ -1527,7 +1534,7 @@ private Function, AbstractResponse> getNodeInfo() { } }; } - + private Function, AbstractResponse> getNodeAPIConfiguration() { return request -> getNodeAPIConfigurationStatement(); } @@ -1545,8 +1552,8 @@ private Function, AbstractResponse> getTips() { private Function, AbstractResponse> getTransactionsToApprove() { return request -> { Optional reference = request.containsKey("reference") ? - Optional.of(HashFactory.TRANSACTION.create(getParameterAsStringAndValidate(request,"reference", HASH_SIZE))) - : Optional.empty(); + Optional.of(HashFactory.TRANSACTION.create(getParameterAsStringAndValidate(request,"reference", HASH_SIZE))) + : Optional.empty(); int depth = getParameterAsInt(request, "depth"); return getTransactionsToApproveStatement(depth, reference); @@ -1599,7 +1606,7 @@ private Function, AbstractResponse> getMissingTransactions() } }; } - + private Function, AbstractResponse> checkConsistency() { return request -> { if (!isNodeSynchronized()) { @@ -1612,7 +1619,7 @@ private Function, AbstractResponse> checkConsistency() { throw new IllegalStateException(e); } }; - } + } private Function, AbstractResponse> wereAddressesSpentFrom() { return request -> { final List addresses = getParameterAsList(request,"addresses", HASH_SIZE); @@ -1637,4 +1644,4 @@ private List convertTrytes(List trytes) { return elements; } -} +} \ No newline at end of file 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 4b2079fd07..61b3fa1de2 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 @@ -1,6 +1,6 @@ package com.iota.iri.service.milestone.impl; -import com.iota.iri.service.validation.TransactionValidator; +import com.iota.iri.service.validation.TransactionSolidifier; import com.iota.iri.model.Hash; import com.iota.iri.service.milestone.MilestoneSolidifier; import com.iota.iri.service.snapshot.SnapshotProvider; @@ -35,7 +35,7 @@ public class MilestoneSolidifierImpl implements MilestoneSolidifier { /** * Defines the interval in which solidity checks are issued (in milliseconds). */ - private static final int SOLIDIFICATION_INTERVAL = 500; + private static final int SOLIDIFICATION_INTERVAL = 100; /** *

@@ -44,7 +44,7 @@ public class MilestoneSolidifierImpl implements MilestoneSolidifier { *

*

* Note: We want to find the next previous milestone and not get stuck somewhere at the end of the tangle with a - * long running {@link TransactionValidator#checkSolidity(Hash)} call. + * long running {@link TransactionSolidifier#checkSolidity(Hash)} call. *

*/ private static final int SOLIDIFICATION_TRANSACTIONS_LIMIT = 50000; @@ -60,9 +60,9 @@ public class MilestoneSolidifierImpl implements MilestoneSolidifier { private final SnapshotProvider snapshotProvider; /** - * Holds a reference to the TransactionValidator which allows us to issue solidity checks. + * Holds a reference to the transactionSolidifier which allows us to issue solidity checks. */ - private final TransactionValidator transactionValidator; + private final TransactionSolidifier transactionSolidifier; /** * Holds a reference to the manager of the background worker. @@ -105,16 +105,16 @@ public class MilestoneSolidifierImpl implements MilestoneSolidifier { /** * @param snapshotProvider snapshot provider which gives us access to the relevant snapshots - * @param transactionValidator TransactionValidator instance that is used by the node + * @param transactionSolidifier transactionSolidifier instance that is used by the node */ - public MilestoneSolidifierImpl(SnapshotProvider snapshotProvider, TransactionValidator transactionValidator) { + public MilestoneSolidifierImpl(SnapshotProvider snapshotProvider, TransactionSolidifier transactionSolidifier) { this.snapshotProvider = snapshotProvider; - this.transactionValidator = transactionValidator; + this.transactionSolidifier = transactionSolidifier; } /** * {@inheritDoc} - * + * *

* Since this method might be called from a performance critical context, we simply add the milestone to a temporary * pool, that gets examined later by the background process. This doesn't just speed up the addition of new jobs but @@ -206,7 +206,7 @@ private void milestoneSolidificationThread() { */ private void processNewlyAddedMilestones() { for (Iterator> iterator = newlyAddedMilestones.entrySet().iterator(); - !Thread.currentThread().isInterrupted() && iterator.hasNext();) { + !Thread.currentThread().isInterrupted() && iterator.hasNext();) { Map.Entry currentEntry = iterator.next(); @@ -232,7 +232,7 @@ private void processNewlyAddedMilestones() { */ private void processSolidificationQueue() { for (Iterator> iterator = milestonesToSolidify.entrySet().iterator(); - !Thread.currentThread().isInterrupted() && iterator.hasNext();) { + !Thread.currentThread().isInterrupted() && iterator.hasNext();) { Map.Entry currentEntry = iterator.next(); @@ -321,7 +321,7 @@ private Map.Entry getNextSolidificationCandidate() { *

*

* It first dumps a log message to keep the node operator informed about the progress of solidification, and then - * issues the {@link TransactionValidator#checkSolidity(Hash, int)} call that starts the solidification + * issues the {@link TransactionSolidifier#checkSolidity(Hash, int)} call that starts the solidification * process. *

*

@@ -341,11 +341,11 @@ private boolean isSolid(Map.Entry currentEntry) { } try { - return transactionValidator.checkSolidity(currentEntry.getKey(), SOLIDIFICATION_TRANSACTIONS_LIMIT); + return transactionSolidifier.addMilestoneToSolidificationQueue(currentEntry.getKey(), SOLIDIFICATION_TRANSACTIONS_LIMIT); } catch (Exception e) { log.error("Error while solidifying milestone #" + currentEntry.getValue(), e); return false; } } -} +} \ No newline at end of file diff --git a/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java b/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java new file mode 100644 index 0000000000..e79098318c --- /dev/null +++ b/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java @@ -0,0 +1,126 @@ +package com.iota.iri.service.validation; + +import com.iota.iri.controllers.TipsViewModel; +import com.iota.iri.controllers.TransactionViewModel; +import com.iota.iri.model.Hash; +import com.iota.iri.service.validation.impl.TransactionSolidifierImpl; +import com.iota.iri.network.TransactionRequester; + +import java.util.Set; + +/** + * Solidification tool. Transactions placed into the solidification queue will be checked for solidity. Any missing + * reference transactions will be placed into the {@link TransactionRequester}. If a transaction is found to be solid + * it is updated as such and placed into the BroadcastQueue to be sent off to the node's neighbours. + */ +public interface TransactionSolidifier { + + /** + * Initialize the executor service. Start processing transactions to solidify. + */ + void start(); + + /** + * Interrupt thread processes and shut down the executor service. + */ + void shutdown(); + + /** + * Add a hash to the solidification queue, and runs an initial {@link #checkSolidity} call. + * + * @param hash Hash of the transaction to solidify + */ + void addToSolidificationQueue(Hash hash); + + /** + * Checks if milestone transaction is solid. Returns true if it is, and if it is not, it adds the hash to the + * solidification queue and returns false. + * + * @param hash Hash of the transaction to solidify + * @param maxToProcess Maximum number of transactions to analyze + * @return True if solid, false if not + */ + boolean addMilestoneToSolidificationQueue(Hash hash, int maxToProcess); + /** + * Fetch a copy of the current transactionsToBroadcast set. + * @return A set of {@link TransactionViewModel} objects to be broadcast. + */ + Set getBroadcastQueue(); + + /** + * Remove any broadcasted transactions from the transactionsToBroadcast set + * @param transactionsBroadcasted A set of {@link TransactionViewModel} objects to remove from the set. + */ + void clearFromBroadcastQueue(Set transactionsBroadcasted); + + /** + * This method does the same as {@link #checkSolidity(Hash, int)} but defaults to an unlimited amount + * of transactions that are allowed to be traversed. + * + * @param hash hash of the transactions that shall get checked + * @return true if the transaction is solid and false otherwise + * @throws Exception if anything goes wrong while trying to solidify the transaction + */ + boolean checkSolidity(Hash hash) throws Exception; + + /** + * This method checks transactions for solidity and marks them accordingly if they are found to be solid. + * + * It iterates through all approved transactions until it finds one that is missing in the database or until it + * reached solid transactions on all traversed subtangles. In case of a missing transactions it issues a transaction + * request and returns false. If no missing transaction is found, it marks the processed transactions as solid in + * the database and returns true. + * + * Since this operation can potentially take a long time to terminate if it would have to traverse big parts of the + * tangle, it is possible to limit the amount of transactions that are allowed to be processed, while looking for + * unsolid / missing approvees. This can be useful when trying to "interrupt" the solidification of one transaction + * (if it takes too many steps) to give another one the chance to be solidified instead (i.e. prevent blocks in the + * solidification threads). + * + * @param hash hash of the transactions that shall get checked + * @param maxProcessedTransactions the maximum amount of transactions that are allowed to be traversed + * @return true if the transaction is solid and false otherwise + * @throws Exception if anything goes wrong while trying to solidify the transaction + */ + boolean checkSolidity(Hash hash, int maxProcessedTransactions) throws Exception; + + /** + * Updates a transaction after it was stored in the tangle. Tells the node to not request the transaction anymore, + * to update the live tips accordingly, and attempts to quickly solidify the transaction. + * + *

+ * Performs the following operations: + * + *

    + *
  1. Removes {@code transactionViewModel}'s hash from the the request queue since we already found it.
  2. + *
  3. If {@code transactionViewModel} has no children (approvers), we add it to the node's active tip list.
  4. + *
  5. Removes {@code transactionViewModel}'s parents (branch & trunk) from the node's tip list + * (if they're present there).
  6. + *
  7. Attempts to quickly solidify {@code transactionViewModel} by checking whether its direct parents + * are solid. If solid we add it to the queue transaction solidification thread to help it propagate the + * solidification to the approving child transactions.
  8. + *
  9. Requests missing direct parent (trunk & branch) transactions that are needed to solidify + * {@code transactionViewModel}.
  10. + *
+ * @param transactionViewModel received transaction that is being updated + * @throws Exception if an error occurred while trying to solidify + * @see TipsViewModel + */ + void updateStatus(TransactionViewModel transactionViewModel) throws Exception; + + /** + * Tries to solidify the transactions quickly by performing {@link TransactionSolidifierImpl#checkApproovee} on + * both parents (trunk and branch). If the parents are solid, mark the transactions as solid. + * @param transactionViewModel transaction to solidify + * @return true if we made the transaction solid, else false. + * @throws Exception + */ + boolean quickSetSolid(TransactionViewModel transactionViewModel) throws Exception; + + /** + * Add to the propagation queue where it will be processed to help solidify approving transactions faster + * @param hash The transaction hash to be removed + * @throws Exception + */ + void addToPropagationQueue(Hash hash) throws Exception; +} \ No newline at end of file diff --git a/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java new file mode 100644 index 0000000000..19ef32f7c8 --- /dev/null +++ b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java @@ -0,0 +1,383 @@ +package com.iota.iri.service.validation.impl; + +import com.google.common.annotations.VisibleForTesting; +import com.iota.iri.controllers.TipsViewModel; +import com.iota.iri.controllers.TransactionViewModel; +import com.iota.iri.model.Hash; +import com.iota.iri.network.pipeline.TransactionProcessingPipeline; +import com.iota.iri.network.TransactionRequester; +import com.iota.iri.service.snapshot.SnapshotProvider; +import com.iota.iri.service.validation.TransactionSolidifier; +import com.iota.iri.storage.Tangle; +import com.iota.iri.utils.log.interval.IntervalLogger; +import com.iota.iri.utils.thread.DedicatedScheduledExecutorService; +import com.iota.iri.utils.thread.SilentScheduledExecutorService; + +import java.util.*; +import java.util.concurrent.*; + +import static com.iota.iri.controllers.TransactionViewModel.PREFILLED_SLOT; +import static com.iota.iri.controllers.TransactionViewModel.fromHash; + +/** + * A solidifier class for processing transactions. Transactions are checked for solidity, and missing transactions are + * subsequently requested. Once a transaction is solidified correctly it is placed into a broadcasting set to be sent to + * neighboring nodes. + */ +public class TransactionSolidifierImpl implements TransactionSolidifier { + + private Tangle tangle; + private SnapshotProvider snapshotProvider; + private TransactionRequester transactionRequester; + + /** + * Max size for all queues. + */ + private static final int MAX_SIZE= 10000; + + private static final int SOLIDIFICATION_INTERVAL = 100; + + private static final IntervalLogger log = new IntervalLogger(TransactionSolidifier.class); + + /** + * Executor service for running the {@link #processTransactionsToSolidify()}. + */ + private SilentScheduledExecutorService executorService = new DedicatedScheduledExecutorService( + "Transaction Solidifier", log.delegate()); + + /** + * A queue for processing transactions with the {@link #checkSolidity(Hash)} call. Once a transaction has been + * marked solid it will be placed into the {@link #transactionsToBroadcast} queue. + */ + private BlockingQueue transactionsToSolidify = new ArrayBlockingQueue<>(MAX_SIZE); + + /** + * A queue for processing transactions with the {@link #propagateSolidTransactions()} call. This will check + * approving transactions with {@link #quickSetSolid(TransactionViewModel)}. + */ + private BlockingQueue solidTransactions = new ArrayBlockingQueue<>(MAX_SIZE); + + /** + * A set of transactions that will be called by the {@link TransactionProcessingPipeline} to be broadcast to + * neighboring nodes. + */ + private BlockingQueue transactionsToBroadcast = new ArrayBlockingQueue<>(MAX_SIZE); + + private TipsViewModel tipsViewModel; + + /** + * Constructor for the solidifier. + * @param tangle The DB reference + * @param snapshotProvider For fetching entry points for solidity checks + * @param transactionRequester A requester for missing transactions + */ + public TransactionSolidifierImpl(Tangle tangle, SnapshotProvider snapshotProvider, TransactionRequester transactionRequester, + TipsViewModel tipsViewModel){ + this.tangle = tangle; + this.snapshotProvider = snapshotProvider; + this.transactionRequester = transactionRequester; + this.tipsViewModel = tipsViewModel; + } + + /** + *{@inheritDoc} + */ + @Override + public void start(){ + executorService.silentScheduleWithFixedDelay(this::processTransactionsToSolidify, 0, + SOLIDIFICATION_INTERVAL, TimeUnit.MILLISECONDS); + } + + /** + *{@inheritDoc} + */ + @Override + public void shutdown() { + executorService.shutdownNow(); + } + + /** + *{@inheritDoc} + */ + @Override + public void addToSolidificationQueue(Hash hash){ + try{ + if(!transactionsToSolidify.contains(hash)) { + if (transactionsToSolidify.size() >= MAX_SIZE - 1) { + transactionsToSolidify.remove(); + } + + transactionsToSolidify.put(hash); + } + } catch(Exception e){ + log.error("Error placing transaction into solidification queue",e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean addMilestoneToSolidificationQueue(Hash hash, int maxToProcess){ + try{ + TransactionViewModel tx = fromHash(tangle, hash); + if(tx.isSolid()){ + addToPropagationQueue(hash); + return true; + } + addToSolidificationQueue(hash); + return false; + }catch(Exception e){ + log.error("Error adding milestone to solidification queue", e); + return false; + } + } + + /** + *{@inheritDoc} + */ + @Override + public Set getBroadcastQueue(){ + return new LinkedHashSet<>(transactionsToBroadcast); + } + + /** + *{@inheritDoc} + */ + @Override + public void clearFromBroadcastQueue(Set transactionsBroadcasted){ + for (TransactionViewModel tvm : transactionsBroadcasted) { + transactionsToBroadcast.remove(tvm); + } + } + + + /** + * Iterate through the {@link #transactionsToSolidify} queue and call {@link #checkSolidity(Hash)} on each hash. + * Solid transactions are then processed into the {@link #transactionsToBroadcast} queue. + */ + private void processTransactionsToSolidify(){ + Hash hash; + if((hash = transactionsToSolidify.poll()) != null) { + try { + checkSolidity(hash); + } catch (Exception e) { + log.info(e.getMessage()); + } + } + propagateSolidTransactions(); + } + + /** + *{@inheritDoc} + */ + @Override + public boolean checkSolidity(Hash hash) throws Exception { + return checkSolidity(hash, 50000); + } + + /** + *{@inheritDoc} + */ + @Override + public boolean checkSolidity(Hash hash, int maxProcessedTransactions) throws Exception { + if(fromHash(tangle, hash).isSolid()) { + return true; + } + LinkedHashSet analyzedHashes = new LinkedHashSet<>(snapshotProvider.getInitialSnapshot().getSolidEntryPoints().keySet()); + if(maxProcessedTransactions != Integer.MAX_VALUE) { + maxProcessedTransactions += analyzedHashes.size(); + } + boolean solid = true; + final Queue nonAnalyzedTransactions = new LinkedList<>(Collections.singleton(hash)); + Hash hashPointer; + while ((hashPointer = nonAnalyzedTransactions.poll()) != null) { + if (!analyzedHashes.add(hashPointer)) { + continue; + } + + if (analyzedHashes.size() >= maxProcessedTransactions) { + return false; + } + + TransactionViewModel transaction = fromHash(tangle, hashPointer); + if (isUnsolidWithoutEntryPoint(transaction, hashPointer)) { + if (transaction.getType() == PREFILLED_SLOT) { + solid = false; + checkRequester(hashPointer); + } else { + nonAnalyzedTransactions.offer(transaction.getTrunkTransactionHash()); + nonAnalyzedTransactions.offer(transaction.getBranchTransactionHash()); + } + } + } + if (solid) { + updateTransactions(analyzedHashes); + } + analyzedHashes.clear(); + return solid; + } + + + /** + * Check if a transaction is present in the {@link #transactionRequester}, if not, it is added. + * @param hashPointer The hash of the transaction to request + */ + private void checkRequester(Hash hashPointer){ + if (!transactionRequester.isTransactionRequested(hashPointer)) { + transactionRequester.requestTransaction(hashPointer); + } + } + + /** + * Iterate through analyzed hashes and place them in the {@link #transactionsToBroadcast} queue + * @param hashes Analyzed hashes from the {@link #checkSolidity(Hash)} call + */ + private void updateTransactions(Set hashes) { + hashes.forEach(hash -> { + try { + TransactionViewModel tvm = fromHash(tangle, hash); + tvm.updateHeights(tangle, snapshotProvider.getInitialSnapshot()); + + if(!tvm.isSolid()){ + tvm.updateSolid(true); + tvm.update(tangle, snapshotProvider.getInitialSnapshot(), "solid|height"); + } + addToBroadcastQueue(tvm); + addToPropagationQueue(tvm.getHash()); + } catch (Exception e) { + log.info(e.getMessage()); + } + }); + } + + /** + * Returns true if transaction is not solid and there are no solid entry points from the initial snapshot. + */ + private boolean isUnsolidWithoutEntryPoint(TransactionViewModel transaction, Hash hashPointer) throws Exception{ + if(!transaction.isSolid() && !snapshotProvider.getInitialSnapshot().hasSolidEntryPoint(hashPointer)){ + return true; + } + addToPropagationQueue(hashPointer); + return false; + } + + + private void addToBroadcastQueue(TransactionViewModel tvm) { + try { + if (transactionsToBroadcast.size() >= MAX_SIZE) { + transactionsToBroadcast.remove(); + } + + transactionsToBroadcast.put(tvm); + } catch(Exception e){ + log.info("Error placing transaction into broadcast queue: " + e.getMessage()); + } + } + + @VisibleForTesting + Set getSolidificationQueue(){ + return new LinkedHashSet<>(transactionsToSolidify); + } + + + @Override + public void updateStatus(TransactionViewModel transactionViewModel) throws Exception { + transactionRequester.clearTransactionRequest(transactionViewModel.getHash()); + if(transactionViewModel.getApprovers(tangle).size() == 0) { + tipsViewModel.addTipHash(transactionViewModel.getHash()); + } + tipsViewModel.removeTipHash(transactionViewModel.getTrunkTransactionHash()); + tipsViewModel.removeTipHash(transactionViewModel.getBranchTransactionHash()); + + if(quickSetSolid(transactionViewModel)) { + transactionViewModel.update(tangle, snapshotProvider.getInitialSnapshot(), "solid|height"); + tipsViewModel.setSolid(transactionViewModel.getHash()); + addToPropagationQueue(transactionViewModel.getHash()); + } + } + + @Override + public void addToPropagationQueue(Hash hash) throws Exception{ + if(!solidTransactions.contains(hash)) { + if (solidTransactions.size() >= MAX_SIZE) { + solidTransactions.poll(); + } + solidTransactions.put(hash); + } + } + + @Override + public boolean quickSetSolid(final TransactionViewModel transactionViewModel) throws Exception { + if(!transactionViewModel.isSolid()) { + boolean solid = true; + if (!checkApproovee(transactionViewModel.getTrunkTransaction(tangle))) { + solid = false; + } + if (!checkApproovee(transactionViewModel.getBranchTransaction(tangle))) { + solid = false; + } + if(solid) { + transactionViewModel.updateSolid(true); + transactionViewModel.updateHeights(tangle, snapshotProvider.getInitialSnapshot()); + addToPropagationQueue(transactionViewModel.getHash()); + addToBroadcastQueue(transactionViewModel); + return true; + } + } + return false; + } + + /** + * If the the {@code approvee} is missing, request it from a neighbor. + * @param approovee transaction we check. + * @return true if {@code approvee} is solid. + * @throws Exception if we encounter an error while requesting a transaction + */ + private boolean checkApproovee(TransactionViewModel approovee) throws Exception { + if(snapshotProvider.getInitialSnapshot().hasSolidEntryPoint(approovee.getHash())) { + return true; + } + if(approovee.getType() == PREFILLED_SLOT) { + // don't solidify from the bottom until cuckoo filters can identify where we deleted -> otherwise we will + // continue requesting old transactions forever + //transactionRequester.requestTransaction(approovee.getHash(), false); + return false; + } + return approovee.isSolid(); + } + + @VisibleForTesting + void propagateSolidTransactions() { + while(!Thread.currentThread().isInterrupted() && solidTransactions.peek() != null) { + try { + Hash hash = solidTransactions.poll(); + TransactionViewModel transaction = fromHash(tangle, hash); + Set approvers = transaction.getApprovers(tangle).getHashes(); + for(Hash h: approvers) { + TransactionViewModel tx = fromHash(tangle, h); + if (quietQuickSetSolid(tx)) { + tx.update(tangle, snapshotProvider.getInitialSnapshot(), "solid|height"); + tipsViewModel.setSolid(h); + } + } + } catch (Exception e) { + log.error("Error while propagating solidity upwards", e); + } + } + } + + /** + * Perform a {@link #quickSetSolid} while capturing and logging errors + * @param transactionViewModel transaction we try to solidify. + * @return true if we managed to solidify, else false. + */ + private boolean quietQuickSetSolid(TransactionViewModel transactionViewModel) { + try { + return quickSetSolid(transactionViewModel); + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } + } +} \ No newline at end of file From c72efc3e694b49d9f0cf2983b6fa8b64ad255542 Mon Sep 17 00:00:00 2001 From: DyrellC Date: Thu, 19 Mar 2020 13:24:29 -0600 Subject: [PATCH 03/18] Update Transaction Validator --- .../validation/TransactionValidator.java | 337 ++---------------- 1 file changed, 21 insertions(+), 316 deletions(-) diff --git a/src/main/java/com/iota/iri/service/validation/TransactionValidator.java b/src/main/java/com/iota/iri/service/validation/TransactionValidator.java index b2c4995c70..cf36e7f94e 100644 --- a/src/main/java/com/iota/iri/service/validation/TransactionValidator.java +++ b/src/main/java/com/iota/iri/service/validation/TransactionValidator.java @@ -2,93 +2,46 @@ import com.google.common.annotations.VisibleForTesting; import com.iota.iri.conf.ProtocolConfig; -import com.iota.iri.controllers.TipsViewModel; import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.crypto.Curl; import com.iota.iri.crypto.Sponge; import com.iota.iri.crypto.SpongeFactory; -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; - -import java.util.*; -import java.util.concurrent.atomic.AtomicBoolean; - -import static com.iota.iri.controllers.TransactionViewModel.*; +/** + * Tool for determining validity of a transaction via a {@link TransactionViewModel}, tryte array or byte array. + */ public class TransactionValidator { - private static final Logger log = LoggerFactory.getLogger(TransactionValidator.class); private static final int TESTNET_MWM_CAP = 13; - public static final int SOLID_SLEEP_TIME = 500; - private final Tangle tangle; private final SnapshotProvider snapshotProvider; - private final TipsViewModel tipsViewModel; private final TransactionRequester transactionRequester; private int minWeightMagnitude = 81; private static final long MAX_TIMESTAMP_FUTURE = 2L * 60L * 60L; private static final long MAX_TIMESTAMP_FUTURE_MS = MAX_TIMESTAMP_FUTURE * 1_000L; - /////////////////////////////////fields for solidification thread////////////////////////////////////// - - private Thread newSolidThread; - - /** - * If true use {@link #newSolidTransactionsOne} while solidifying. Else use {@link #newSolidTransactionsTwo}. - */ - private final AtomicBoolean useFirst = new AtomicBoolean(true); - /** - * Is {@link #newSolidThread} shutting down - */ - private final AtomicBoolean shuttingDown = new AtomicBoolean(false); - /** - * mutex for solidification - */ - private final Object cascadeSync = new Object(); - private final Set newSolidTransactionsOne = new LinkedHashSet<>(); - private final Set newSolidTransactionsTwo = new LinkedHashSet<>(); - /** * Constructor for Tangle Validator * - * @param tangle relays tangle data to and from the persistence layer * @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 * @param protocolConfig used for checking if we are in testnet and mwm. testnet true if we are in testnet * mode, this caps {@code mwm} to {@value #TESTNET_MWM_CAP} regardless of parameter input. * minimum weight magnitude: the minimal number of 9s that ought to appear at the end of the * transaction hash */ - public TransactionValidator(Tangle tangle, SnapshotProvider snapshotProvider, TipsViewModel tipsViewModel, TransactionRequester transactionRequester, ProtocolConfig protocolConfig) { - this.tangle = tangle; + public TransactionValidator(SnapshotProvider snapshotProvider, TransactionRequester transactionRequester, ProtocolConfig protocolConfig) { this.snapshotProvider = snapshotProvider; - this.tipsViewModel = tipsViewModel; this.transactionRequester = transactionRequester; - this.newSolidThread = new Thread(spawnSolidTransactionsPropagation(), "Solid TX cascader"); setMwm(protocolConfig.isTestnet(), protocolConfig.getMwm()); } /** - * Does two things: - *
    - *
  1. Sets the minimum weight magnitude (MWM). POW on a transaction is validated by counting a certain - * number of consecutive 9s in the end of the transaction hash. The number of 9s is the MWM.
  2. - *
  3. Starts the transaction solidification thread.
  4. - *
- * - * - * @see #spawnSolidTransactionsPropagation() + * Set the Minimum Weight Magnitude for validation checks. */ - public void init() { - newSolidThread.start(); - } - @VisibleForTesting void setMwm(boolean testnet, int mwm) { minWeightMagnitude = mwm; @@ -99,16 +52,6 @@ void setMwm(boolean testnet, int mwm) { } } - /** - * Shutdown roots to tip solidification thread - * @throws InterruptedException - * @see #spawnSolidTransactionsPropagation() - */ - public void shutdown() throws InterruptedException { - shuttingDown.set(true); - newSolidThread.join(); - } - /** * @return the minimal number of trailing 9s that have to be present at the end of the transaction hash * in order to validate that sufficient proof of work has been done @@ -163,18 +106,14 @@ private boolean hasInvalidTimestamp(TransactionViewModel transactionViewModel) { public void runValidation(TransactionViewModel transactionViewModel, final int minWeightMagnitude) { transactionViewModel.setMetadata(); transactionViewModel.setAttachmentData(); - if(hasInvalidTimestamp(transactionViewModel)) { + if (hasInvalidTimestamp(transactionViewModel)) { throw new StaleTimestampException("Invalid transaction timestamp."); } - for (int i = VALUE_TRINARY_OFFSET + VALUE_USABLE_TRINARY_SIZE; i < VALUE_TRINARY_OFFSET + VALUE_TRINARY_SIZE; i++) { - if (transactionViewModel.trits()[i] != 0) { - throw new IllegalStateException("Invalid transaction value"); - } - } + confirmValidTransactionValues(transactionViewModel); int weightMagnitude = transactionViewModel.weightMagnitude; - if(weightMagnitude < minWeightMagnitude) { - throw new IllegalStateException("Invalid weight magnitude"); + if (weightMagnitude < minWeightMagnitude) { + throw new IllegalStateException("Invalid transaction hash"); } if (transactionViewModel.value() != 0 && transactionViewModel.getAddressHash().trits()[Curl.HASH_LENGTH - 1] != 0) { @@ -182,6 +121,15 @@ public void runValidation(TransactionViewModel transactionViewModel, final int m } } + private void confirmValidTransactionValues(TransactionViewModel transactionViewModel) throws IllegalStateException { + for (int i = TransactionViewModel.VALUE_TRINARY_OFFSET + TransactionViewModel.VALUE_USABLE_TRINARY_SIZE; + i < TransactionViewModel.VALUE_TRINARY_OFFSET + TransactionViewModel.VALUE_TRINARY_SIZE; i++) { + if (transactionViewModel.trits()[i] != 0) { + throw new IllegalStateException("Invalid transaction value"); + } + } + } + /** * Creates a new transaction from {@code trits} and validates it with {@link #runValidation}. * @@ -205,255 +153,12 @@ public TransactionViewModel validateTrits(final byte[] trits, int minWeightMagni * @throws RuntimeException if validation fails */ public TransactionViewModel validateBytes(final byte[] bytes, int minWeightMagnitude, Sponge curl) { - TransactionViewModel transactionViewModel = new TransactionViewModel(bytes, TransactionHash.calculate(bytes, TRINARY_SIZE, curl)); + TransactionViewModel transactionViewModel = new TransactionViewModel(bytes, TransactionHash.calculate(bytes, + TransactionViewModel.TRINARY_SIZE, curl)); runValidation(transactionViewModel, minWeightMagnitude); return transactionViewModel; } - /** - * This method does the same as {@link #checkSolidity(Hash, int)} but defaults to an unlimited amount - * of transactions that are allowed to be traversed. - * - * @param hash hash of the transactions that shall get checked - * @return true if the transaction is solid and false otherwise - * @throws Exception if anything goes wrong while trying to solidify the transaction - */ - public boolean checkSolidity(Hash hash) throws Exception { - return checkSolidity(hash, Integer.MAX_VALUE); - } - - /** - * This method checks transactions for solidity and marks them accordingly if they are found to be solid. - * - * It iterates through all approved transactions until it finds one that is missing in the database or until it - * reached solid transactions on all traversed subtangles. In case of a missing transactions it issues a transaction - * request and returns false. If no missing transaction is found, it marks the processed transactions as solid in - * the database and returns true. - * - * Since this operation can potentially take a long time to terminate if it would have to traverse big parts of the - * tangle, it is possible to limit the amount of transactions that are allowed to be processed, while looking for - * unsolid / missing approvees. This can be useful when trying to "interrupt" the solidification of one transaction - * (if it takes too many steps) to give another one the chance to be solidified instead (i.e. prevent blocks in the - * solidification threads). - * - * @param hash hash of the transactions that shall get checked - * @param maxProcessedTransactions the maximum amount of transactions that are allowed to be traversed - * @return true if the transaction is solid and false otherwise - * @throws Exception if anything goes wrong while trying to solidify the transaction - */ - public boolean checkSolidity(Hash hash, int maxProcessedTransactions) throws Exception { - if(fromHash(tangle, hash).isSolid()) { - return true; - } - LinkedHashSet analyzedHashes = new LinkedHashSet<>(snapshotProvider.getInitialSnapshot().getSolidEntryPoints().keySet()); - if(maxProcessedTransactions != Integer.MAX_VALUE) { - maxProcessedTransactions += analyzedHashes.size(); - } - boolean solid = true; - final Queue nonAnalyzedTransactions = new LinkedList<>(Collections.singleton(hash)); - Hash hashPointer; - while ((hashPointer = nonAnalyzedTransactions.poll()) != null) { - if (!analyzedHashes.add(hashPointer)) { - continue; - } - - if (analyzedHashes.size() >= maxProcessedTransactions) { - return false; - } - - TransactionViewModel transaction = fromHash(tangle, hashPointer); - if (!transaction.isSolid() && !snapshotProvider.getInitialSnapshot().hasSolidEntryPoint(hashPointer)) { - if (transaction.getType() == PREFILLED_SLOT) { - solid = false; - - if (!transactionRequester.isTransactionRequested(hashPointer)) { - transactionRequester.requestTransaction(hashPointer); - continue; - } - } else { - nonAnalyzedTransactions.offer(transaction.getTrunkTransactionHash()); - nonAnalyzedTransactions.offer(transaction.getBranchTransactionHash()); - } - } - } - if (solid) { - updateSolidTransactions(tangle, snapshotProvider.getInitialSnapshot(), analyzedHashes); - } - analyzedHashes.clear(); - return solid; - } - - public void addSolidTransaction(Hash hash) { - synchronized (cascadeSync) { - if (useFirst.get()) { - newSolidTransactionsOne.add(hash); - } else { - newSolidTransactionsTwo.add(hash); - } - } - } - - /** - * Creates a runnable that runs {@link #propagateSolidTransactions()} in a loop every {@value #SOLID_SLEEP_TIME} ms - * @return runnable that is not started - */ - private Runnable spawnSolidTransactionsPropagation() { - return () -> { - while(!shuttingDown.get()) { - propagateSolidTransactions(); - try { - Thread.sleep(SOLID_SLEEP_TIME); - } catch (InterruptedException e) { - // Ignoring InterruptedException. Do not use Thread.currentThread().interrupt() here. - log.error("Thread was interrupted: ", e); - } - } - }; - } - - /** - * Iterates over all currently known solid transactions. For each solid transaction, we find - * its children (approvers) and try to quickly solidify them with {@link #quietQuickSetSolid}. - * If we manage to solidify the transactions, we add them to the solidification queue for a traversal by a later run. - */ - @VisibleForTesting - void propagateSolidTransactions() { - Set newSolidHashes = new HashSet<>(); - useFirst.set(!useFirst.get()); - //synchronized to make sure no one is changing the newSolidTransactions collections during addAll - synchronized (cascadeSync) { - //We are using a collection that doesn't get updated by other threads - if (useFirst.get()) { - newSolidHashes.addAll(newSolidTransactionsTwo); - newSolidTransactionsTwo.clear(); - } else { - newSolidHashes.addAll(newSolidTransactionsOne); - newSolidTransactionsOne.clear(); - } - } - Iterator cascadeIterator = newSolidHashes.iterator(); - while(cascadeIterator.hasNext() && !shuttingDown.get()) { - try { - Hash hash = cascadeIterator.next(); - TransactionViewModel transaction = fromHash(tangle, hash); - Set approvers = transaction.getApprovers(tangle).getHashes(); - for(Hash h: approvers) { - TransactionViewModel tx = fromHash(tangle, h); - if(quietQuickSetSolid(tx)) { - tx.update(tangle, snapshotProvider.getInitialSnapshot(), "solid|height"); - tipsViewModel.setSolid(h); - addSolidTransaction(h); - } - } - } catch (Exception e) { - log.error("Error while propagating solidity upwards", e); - } - } - } - - - /** - * Updates a transaction after it was stored in the tangle. Tells the node to not request the transaction anymore, - * to update the live tips accordingly, and attempts to quickly solidify the transaction. - * - *

- * Performs the following operations: - * - *

    - *
  1. Removes {@code transactionViewModel}'s hash from the the request queue since we already found it.
  2. - *
  3. If {@code transactionViewModel} has no children (approvers), we add it to the node's active tip list.
  4. - *
  5. Removes {@code transactionViewModel}'s parents (branch & trunk) from the node's tip list - * (if they're present there).
  6. - *
  7. Attempts to quickly solidify {@code transactionViewModel} by checking whether its direct parents - * are solid. If solid we add it to the queue transaction solidification thread to help it propagate the - * solidification to the approving child transactions.
  8. - *
  9. Requests missing direct parent (trunk & branch) transactions that are needed to solidify - * {@code transactionViewModel}.
  10. - *
- * @param transactionViewModel received transaction that is being updated - * @throws Exception if an error occurred while trying to solidify - * @see TipsViewModel - */ - //Not part of the validation process. This should be moved to a component in charge of - //what transaction we gossip. - public void updateStatus(TransactionViewModel transactionViewModel) throws Exception { - transactionRequester.clearTransactionRequest(transactionViewModel.getHash()); - if(transactionViewModel.getApprovers(tangle).size() == 0) { - tipsViewModel.addTipHash(transactionViewModel.getHash()); - } - tipsViewModel.removeTipHash(transactionViewModel.getTrunkTransactionHash()); - tipsViewModel.removeTipHash(transactionViewModel.getBranchTransactionHash()); - - if(quickSetSolid(transactionViewModel)) { - transactionViewModel.update(tangle, snapshotProvider.getInitialSnapshot(), "solid|height"); - tipsViewModel.setSolid(transactionViewModel.getHash()); - addSolidTransaction(transactionViewModel.getHash()); - } - } - - /** - * Perform a {@link #quickSetSolid} while capturing and logging errors - * @param transactionViewModel transaction we try to solidify. - * @return true if we managed to solidify, else false. - */ - private boolean quietQuickSetSolid(TransactionViewModel transactionViewModel) { - try { - return quickSetSolid(transactionViewModel); - } catch (Exception e) { - log.error(e.getMessage(), e); - return false; - } - } - - /** - * Tries to solidify the transactions quickly by performing {@link #checkApproovee} on both parents (trunk and - * branch). If the parents are solid, mark the transactions as solid. - * @param transactionViewModel transaction to solidify - * @return true if we made the transaction solid, else false. - * @throws Exception - */ - private boolean quickSetSolid(final TransactionViewModel transactionViewModel) throws Exception { - if(!transactionViewModel.isSolid()) { - boolean solid = true; - if (!checkApproovee(transactionViewModel.getTrunkTransaction(tangle))) { - solid = false; - } - if (!checkApproovee(transactionViewModel.getBranchTransaction(tangle))) { - solid = false; - } - if(solid) { - transactionViewModel.updateSolid(true); - transactionViewModel.updateHeights(tangle, snapshotProvider.getInitialSnapshot()); - return true; - } - } - return false; - } - - /** - * If the the {@code approvee} is missing, request it from a neighbor. - * @param approovee transaction we check. - * @return true if {@code approvee} is solid. - * @throws Exception if we encounter an error while requesting a transaction - */ - private boolean checkApproovee(TransactionViewModel approovee) throws Exception { - if(snapshotProvider.getInitialSnapshot().hasSolidEntryPoint(approovee.getHash())) { - return true; - } - if(approovee.getType() == PREFILLED_SLOT) { - // don't solidify from the bottom until cuckoo filters can identify where we deleted -> otherwise we will - // continue requesting old transactions forever - //transactionRequester.requestTransaction(approovee.getHash(), false); - return false; - } - return approovee.isSolid(); - } - - @VisibleForTesting - boolean isNewSolidTxSetsEmpty () { - return newSolidTransactionsOne.isEmpty() && newSolidTransactionsTwo.isEmpty(); - } - /** * Thrown if transaction fails {@link #hasInvalidTimestamp} check. */ @@ -462,4 +167,4 @@ public static class StaleTimestampException extends RuntimeException { super(message); } } -} +} \ No newline at end of file From b6a4e4802959069782f48993da715bed698537a3 Mon Sep 17 00:00:00 2001 From: DyrellC Date: Thu, 19 Mar 2020 13:30:29 -0600 Subject: [PATCH 04/18] Update Unit Tests --- .../NetworkInjectionConfigurationTest.java | 7 + .../network/pipeline/ReceivedStageTest.java | 24 +-- .../network/pipeline/SolidifyStageTest.java | 115 +++++++++++++ .../TransactionProcessingPipelineTest.java | 46 ++++- .../java/com/iota/iri/service/APITest.java | 6 +- .../com/iota/iri/service/ApiCallTest.java | 2 +- .../validation/TransactionValidatorTest.java | 154 ++--------------- .../impl/TransactionSolidifierImplTest.java | 160 ++++++++++++++++++ 8 files changed, 353 insertions(+), 161 deletions(-) create mode 100644 src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java create mode 100644 src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java diff --git a/src/test/java/com/iota/iri/network/NetworkInjectionConfigurationTest.java b/src/test/java/com/iota/iri/network/NetworkInjectionConfigurationTest.java index 6cb4f26b8e..5affbf062e 100644 --- a/src/test/java/com/iota/iri/network/NetworkInjectionConfigurationTest.java +++ b/src/test/java/com/iota/iri/network/NetworkInjectionConfigurationTest.java @@ -3,6 +3,7 @@ import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; +import com.iota.iri.service.validation.TransactionSolidifier; import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.conf.BaseIotaConfig; import com.iota.iri.conf.IotaConfig; @@ -37,6 +38,11 @@ public void provideTransactionProcessingPipeline() { assertNotNull("instance creation did not work", testInjector().getInstance(TransactionProcessingPipeline.class)); } + @Test + public void provideTransactionSolidifier(){ + assertNotNull("instance creation did not work", testInjector().getInstance(TransactionSolidifier.class)); + } + private Injector testInjector() { IotaConfig config = mock(IotaConfig.class); when(config.getCoordinator()).thenReturn(BaseIotaConfig.Defaults.COORDINATOR); @@ -50,6 +56,7 @@ protected void configure() { bind(LatestMilestoneTracker.class).toInstance(mock(LatestMilestoneTracker.class)); bind(SnapshotProvider.class).toInstance(mock(SnapshotProvider.class)); bind(TransactionValidator.class).toInstance(mock(TransactionValidator.class)); + bind(TransactionSolidifier.class).toInstance(mock(TransactionSolidifier.class)); } } diff --git a/src/test/java/com/iota/iri/network/pipeline/ReceivedStageTest.java b/src/test/java/com/iota/iri/network/pipeline/ReceivedStageTest.java index 01e9746782..6ab434e8b6 100644 --- a/src/test/java/com/iota/iri/network/pipeline/ReceivedStageTest.java +++ b/src/test/java/com/iota/iri/network/pipeline/ReceivedStageTest.java @@ -1,6 +1,6 @@ package com.iota.iri.network.pipeline; -import com.iota.iri.service.validation.TransactionValidator; +import com.iota.iri.service.validation.TransactionSolidifier; import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.network.TransactionRequester; import com.iota.iri.network.neighbor.Neighbor; @@ -26,7 +26,7 @@ public class ReceivedStageTest { private Tangle tangle; @Mock - private TransactionValidator transactionValidator; + private TransactionSolidifier transactionSolidifier; @Mock private SnapshotProvider snapshotProvider; @@ -49,7 +49,7 @@ public void newlyStoredTransactionUpdatesAlsoArrivalTimeAndSender() throws Excep Mockito.when(neighbor.getMetrics()).thenReturn(neighborMetrics); Mockito.when(transactionRequester.removeRecentlyRequestedTransaction(Mockito.any())).thenReturn(true); - ReceivedStage stage = new ReceivedStage(tangle, transactionValidator, snapshotProvider, transactionRequester); + ReceivedStage stage = new ReceivedStage(tangle, transactionSolidifier, snapshotProvider, transactionRequester); ReceivedPayload receivedPayload = new ReceivedPayload(neighbor, tvm); ProcessingContext ctx = new ProcessingContext(null, receivedPayload); stage.process(ctx); @@ -58,11 +58,11 @@ public void newlyStoredTransactionUpdatesAlsoArrivalTimeAndSender() throws Excep Mockito.verify(tvm).update(Mockito.any(), Mockito.any(), Mockito.any()); Mockito.verify(transactionRequester).removeRecentlyRequestedTransaction(Mockito.any()); Mockito.verify(transactionRequester).requestTrunkAndBranch(Mockito.any()); - assertEquals("should submit to broadcast stage next", TransactionProcessingPipeline.Stage.BROADCAST, + assertEquals("should submit to broadcast stage next", TransactionProcessingPipeline.Stage.SOLIDIFY, ctx.getNextStage()); - BroadcastPayload broadcastPayload = (BroadcastPayload) ctx.getPayload(); - assertEquals("neighbor is still the same", neighbor, broadcastPayload.getOriginNeighbor()); - assertEquals("tvm is still the same", tvm, broadcastPayload.getTransactionViewModel()); + SolidifyPayload solidifyPayload = (SolidifyPayload) ctx.getPayload(); + assertEquals("neighbor is still the same", neighbor, solidifyPayload.getOriginNeighbor()); + assertEquals("tvm is still the same", tvm, solidifyPayload.getTransaction()); } @Test @@ -70,7 +70,7 @@ public void alreadyStoredTransactionDoesNoUpdates() throws Exception { Mockito.when(tvm.store(tangle, snapshotProvider.getInitialSnapshot())).thenReturn(false); Mockito.when(neighbor.getMetrics()).thenReturn(neighborMetrics); - ReceivedStage stage = new ReceivedStage(tangle, transactionValidator, snapshotProvider, transactionRequester); + ReceivedStage stage = new ReceivedStage(tangle, transactionSolidifier, snapshotProvider, transactionRequester); ReceivedPayload receivedPayload = new ReceivedPayload(neighbor, tvm); ProcessingContext ctx = new ProcessingContext(null, receivedPayload); stage.process(ctx); @@ -79,11 +79,11 @@ public void alreadyStoredTransactionDoesNoUpdates() throws Exception { Mockito.verify(tvm, Mockito.never()).update(Mockito.any(), Mockito.any(), Mockito.any()); Mockito.verify(transactionRequester).removeRecentlyRequestedTransaction(Mockito.any()); Mockito.verify(transactionRequester, Mockito.never()).requestTrunkAndBranch(Mockito.any()); - assertEquals("should submit to broadcast stage next", TransactionProcessingPipeline.Stage.BROADCAST, + assertEquals("should submit to broadcast stage next", TransactionProcessingPipeline.Stage.SOLIDIFY, ctx.getNextStage()); - BroadcastPayload broadcastPayload = (BroadcastPayload) ctx.getPayload(); - assertEquals("neighbor should still be the same", neighbor, broadcastPayload.getOriginNeighbor()); - assertEquals("tvm should still be the same", tvm, broadcastPayload.getTransactionViewModel()); + SolidifyPayload solidifyPayload = (SolidifyPayload) ctx.getPayload(); + assertEquals("neighbor should still be the same", neighbor, solidifyPayload.getOriginNeighbor()); + assertEquals("tvm should still be the same", tvm, solidifyPayload.getTransaction()); } } \ No newline at end of file diff --git a/src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java b/src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java new file mode 100644 index 0000000000..22f62411dc --- /dev/null +++ b/src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java @@ -0,0 +1,115 @@ +package com.iota.iri.network.pipeline; + +import com.iota.iri.controllers.TipsViewModel; +import com.iota.iri.controllers.TransactionViewModel; +import com.iota.iri.model.Hash; +import com.iota.iri.model.persistables.Transaction; +import com.iota.iri.service.validation.TransactionSolidifier; +import com.iota.iri.storage.Tangle; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import static org.junit.Assert.assertEquals; + +public class SolidifyStageTest { + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private Tangle tangle; + + @Mock + private TipsViewModel tipsViewModel; + + @Mock + private TransactionViewModel tvm; + + @Mock + private Hash originalHash; + + @Mock + private Hash tipHash; + + @Mock + private TransactionSolidifier transactionSolidifier; + + @Test + public void solidTransactionIsBroadcast() throws Exception{ + Mockito.when(tvm.isSolid()).thenReturn(true); + Mockito.when(tvm.getHash()).thenReturn(originalHash); + + SolidifyStage solidifyStage = new SolidifyStage(transactionSolidifier, tipsViewModel, tangle); + SolidifyPayload solidifyPayload = new SolidifyPayload(null, tvm); + ProcessingContext ctx = new ProcessingContext(solidifyPayload); + + solidifyStage.process(ctx); + Thread.sleep(100); + + assertEquals("Expected next stage to be broadcast", ctx.getNextStage(), + TransactionProcessingPipeline.Stage.BROADCAST); + BroadcastPayload broadcastPayload = (BroadcastPayload) ctx.getPayload(); + assertEquals("Expected payload hash to equal the original transaction hash", + broadcastPayload.getTransactionViewModel().getHash(), originalHash); + } + + @Test + public void quickSetSolidTransactionIsBroadcast() throws Exception{ + Mockito.when(transactionSolidifier.quickSetSolid(tvm)).thenReturn(true); + Mockito.when(tvm.getHash()).thenReturn(originalHash); + + SolidifyStage solidifyStage = new SolidifyStage(transactionSolidifier, tipsViewModel, tangle); + SolidifyPayload solidifyPayload = new SolidifyPayload(null, tvm); + ProcessingContext ctx = new ProcessingContext(solidifyPayload); + + solidifyStage.process(ctx); + Thread.sleep(100); + + assertEquals("Expected next stage to be broadcast", ctx.getNextStage(), + TransactionProcessingPipeline.Stage.BROADCAST); + BroadcastPayload broadcastPayload = (BroadcastPayload) ctx.getPayload(); + assertEquals("Expected payload hash to equal the original transaction hash", + broadcastPayload.getTransactionViewModel().getHash(), originalHash); + } + + @Test + public void unsolidTransactionBroadcastsRandomSolidTip() throws Exception{ + Mockito.when(tvm.isSolid()).thenReturn(false); + Mockito.when(transactionSolidifier.quickSetSolid(tvm)).thenReturn(false); + TransactionViewModel tip = new TransactionViewModel(new Transaction(), tipHash); + + SolidifyStage solidifyStage = new SolidifyStage(transactionSolidifier, tipsViewModel, tangle); + SolidifyPayload solidifyPayload = new SolidifyPayload(null, tvm); + ProcessingContext ctx = new ProcessingContext(solidifyPayload); + + solidifyStage.injectTip(tip); + solidifyStage.process(ctx); + Thread.sleep(100); + + assertEquals("Expected next stage to be broadcast", ctx.getNextStage(), + TransactionProcessingPipeline.Stage.BROADCAST); + BroadcastPayload broadcastPayload = (BroadcastPayload) ctx.getPayload(); + assertEquals("Expected payload hash to equal random tip hash", + broadcastPayload.getTransactionViewModel().getHash(), tipHash); + } + + @Test + public void unsolidWithNoRandomTipsAborts() throws Exception{ + Mockito.when(tvm.isSolid()).thenReturn(false); + Mockito.when(transactionSolidifier.quickSetSolid(tvm)).thenReturn(false); + Mockito.when(tipsViewModel.getRandomSolidTipHash()).thenReturn(null); + + SolidifyStage solidifyStage = new SolidifyStage(transactionSolidifier, tipsViewModel, tangle); + SolidifyPayload solidifyPayload = new SolidifyPayload(null, tvm); + ProcessingContext ctx = new ProcessingContext(solidifyPayload); + + solidifyStage.process(ctx); + Thread.sleep(100); + + assertEquals("Expected next stage to be broadcast", ctx.getNextStage(), + TransactionProcessingPipeline.Stage.FINISH); + } +} \ No newline at end of file diff --git a/src/test/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineTest.java b/src/test/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineTest.java index a88daa900e..576f418bc0 100644 --- a/src/test/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineTest.java +++ b/src/test/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineTest.java @@ -1,5 +1,6 @@ package com.iota.iri.network.pipeline; +import com.iota.iri.service.validation.TransactionSolidifier; import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.conf.NodeConfig; import com.iota.iri.controllers.TipsViewModel; @@ -18,6 +19,7 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; + public class TransactionProcessingPipelineTest { @Rule @@ -74,6 +76,15 @@ public class TransactionProcessingPipelineTest { @Mock private HashingPayload hashingPayload; + @Mock + private BroadcastPayload broadcastPayload; + + @Mock + private SolidifyStage solidifyStage; + + @Mock + private SolidifyPayload solidifyPayload; + @Mock private ProcessingContext validationCtx; @@ -89,9 +100,15 @@ public class TransactionProcessingPipelineTest { @Mock private ProcessingContext broadcastCtx; + @Mock + private ProcessingContext solidifyCtx; + @Mock private ProcessingContext abortCtx; + @Mock + private TransactionSolidifier transactionSolidifier; + private void mockHashingStage(TransactionProcessingPipeline pipeline) { Mockito.when(hashingPayload.getTxTrits()).thenReturn(null); Mockito.doAnswer(invocation -> { @@ -107,6 +124,7 @@ private void injectMockedStagesIntoPipeline(TransactionProcessingPipeline pipeli pipeline.setHashingStage(hashingStage); pipeline.setReplyStage(replyStage); pipeline.setValidationStage(validationStage); + pipeline.setSolidifyStage(solidifyStage); } @Test @@ -114,7 +132,7 @@ public void processingAValidNewTransactionFlowsThroughTheEntirePipeline() throws TransactionProcessingPipeline pipeline = new TransactionProcessingPipelineImpl(neighborRouter, nodeConfig, transactionValidator, tangle, snapshotProvider, tipsViewModel, latestMilestoneTracker, - transactionRequester); + transactionRequester, transactionSolidifier); // inject mocks injectMockedStagesIntoPipeline(pipeline); @@ -136,7 +154,13 @@ public void processingAValidNewTransactionFlowsThroughTheEntirePipeline() throws // mock received Mockito.when(broadcastCtx.getNextStage()).thenReturn(TransactionProcessingPipeline.Stage.BROADCAST); - Mockito.when(receivedStage.process(receivedCtx)).thenReturn(broadcastCtx); + Mockito.when(broadcastCtx.getPayload()).thenReturn(broadcastPayload); + Mockito.when(receivedStage.process(receivedCtx)).thenReturn(solidifyCtx); + + // mock solidify + Mockito.when(solidifyCtx.getPayload()).thenReturn(solidifyPayload); + Mockito.when(solidifyCtx.getNextStage()).thenReturn(TransactionProcessingPipeline.Stage.SOLIDIFY); + Mockito.when(solidifyStage.process(solidifyCtx)).thenReturn(broadcastCtx); pipeline.start(); @@ -152,6 +176,7 @@ public void processingAValidNewTransactionFlowsThroughTheEntirePipeline() throws Mockito.verify(validationStage).process(Mockito.any()); Mockito.verify(receivedStage).process(Mockito.any()); Mockito.verify(replyStage).process(Mockito.any()); + Mockito.verify(solidifyStage).process(Mockito.any()); Mockito.verify(broadcastStage).process(Mockito.any()); } @@ -159,7 +184,7 @@ public void processingAValidNewTransactionFlowsThroughTheEntirePipeline() throws public void processingAKnownTransactionOnlyFlowsToTheReplyStage() throws InterruptedException { TransactionProcessingPipeline pipeline = new TransactionProcessingPipelineImpl(neighborRouter, nodeConfig, transactionValidator, tangle, snapshotProvider, tipsViewModel, latestMilestoneTracker, - transactionRequester); + transactionRequester, transactionSolidifier); // inject mocks pipeline.setPreProcessStage(preProcessStage); @@ -181,6 +206,7 @@ public void processingAKnownTransactionOnlyFlowsToTheReplyStage() throws Interru Mockito.verify(hashingStage, Mockito.never()).process(Mockito.any()); Mockito.verify(validationStage, Mockito.never()).process(Mockito.any()); Mockito.verify(receivedStage, Mockito.never()).process(Mockito.any()); + Mockito.verify(solidifyStage, Mockito.never()).process(Mockito.any()); Mockito.verify(broadcastStage, Mockito.never()).process(Mockito.any()); // should have called @@ -193,7 +219,7 @@ public void processingAValidNewTransactionNotOriginatingFromANeighborFlowsThroug throws InterruptedException { TransactionProcessingPipeline pipeline = new TransactionProcessingPipelineImpl(neighborRouter, nodeConfig, transactionValidator, tangle, snapshotProvider, tipsViewModel, latestMilestoneTracker, - transactionRequester); + transactionRequester, transactionSolidifier); // inject mocks injectMockedStagesIntoPipeline(pipeline); @@ -202,7 +228,6 @@ public void processingAValidNewTransactionNotOriginatingFromANeighborFlowsThroug Mockito.when(hashingCtx.getNextStage()).thenReturn(TransactionProcessingPipeline.Stage.HASHING); Mockito.when(hashingCtx.getPayload()).thenReturn(hashingPayload); - // mock hashing context/stage // mock hashing context/stage mockHashingStage(pipeline); @@ -212,7 +237,12 @@ public void processingAValidNewTransactionNotOriginatingFromANeighborFlowsThroug // mock received Mockito.when(broadcastCtx.getNextStage()).thenReturn(TransactionProcessingPipeline.Stage.BROADCAST); - Mockito.when(receivedStage.process(receivedCtx)).thenReturn(broadcastCtx); + Mockito.when(receivedStage.process(receivedCtx)).thenReturn(solidifyCtx); + + // mock solidify + Mockito.when(solidifyCtx.getPayload()).thenReturn(solidifyPayload); + Mockito.when(solidifyCtx.getNextStage()).thenReturn(TransactionProcessingPipeline.Stage.SOLIDIFY); + Mockito.when(solidifyStage.process(solidifyCtx)).thenReturn(broadcastCtx); pipeline.start(); @@ -231,6 +261,7 @@ public void processingAValidNewTransactionNotOriginatingFromANeighborFlowsThroug Mockito.verify(hashingStage).process(Mockito.any()); Mockito.verify(validationStage).process(Mockito.any()); Mockito.verify(receivedStage).process(Mockito.any()); + Mockito.verify(solidifyStage).process(Mockito.any()); Mockito.verify(broadcastStage).process(Mockito.any()); } @@ -238,7 +269,7 @@ public void processingAValidNewTransactionNotOriginatingFromANeighborFlowsThroug public void anInvalidNewTransactionStopsBeingProcessedAfterTheValidationStage() throws InterruptedException { TransactionProcessingPipeline pipeline = new TransactionProcessingPipelineImpl(neighborRouter, nodeConfig, transactionValidator, tangle, snapshotProvider, tipsViewModel, latestMilestoneTracker, - transactionRequester); + transactionRequester, transactionSolidifier); // inject mocks injectMockedStagesIntoPipeline(pipeline); @@ -269,6 +300,7 @@ public void anInvalidNewTransactionStopsBeingProcessedAfterTheValidationStage() Mockito.verify(preProcessStage, Mockito.never()).process(Mockito.any()); Mockito.verify(broadcastStage, Mockito.never()).process(Mockito.any()); Mockito.verify(receivedStage, Mockito.never()).process(Mockito.any()); + Mockito.verify(solidifyStage, Mockito.never()).process(Mockito.any()); // should have called Mockito.verify(hashingStage).process(Mockito.any()); diff --git a/src/test/java/com/iota/iri/service/APITest.java b/src/test/java/com/iota/iri/service/APITest.java index 9643959f54..7169f339ad 100644 --- a/src/test/java/com/iota/iri/service/APITest.java +++ b/src/test/java/com/iota/iri/service/APITest.java @@ -1,5 +1,6 @@ package com.iota.iri.service; +import com.iota.iri.service.validation.TransactionSolidifier; import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.conf.IotaConfig; import com.iota.iri.controllers.TransactionViewModel; @@ -28,6 +29,9 @@ public class APITest { @Mock(answer = Answers.RETURNS_SMART_NULLS) private TransactionValidator transactionValidator; + @Mock + private TransactionSolidifier transactionSolidifier; + @Mock private SnapshotProvider snapshotProvider; @@ -43,7 +47,7 @@ public void whenStoreTransactionsStatementThenSetArrivalTimeToCurrentMillis() th API api = new API(config, null, null, null, null, null, snapshotProvider, null, null, null, null, - transactionValidator, null, null); + transactionValidator, null, null, transactionSolidifier); api.storeTransactionsStatement(Collections.singletonList("FOO")); diff --git a/src/test/java/com/iota/iri/service/ApiCallTest.java b/src/test/java/com/iota/iri/service/ApiCallTest.java index a6f4f0de39..d2ca7dc9b0 100644 --- a/src/test/java/com/iota/iri/service/ApiCallTest.java +++ b/src/test/java/com/iota/iri/service/ApiCallTest.java @@ -14,7 +14,7 @@ public class ApiCallTest { @Before public void setUp() { IotaConfig configuration = Mockito.mock(IotaConfig.class); - api = new API(configuration, null, null, null, null, null, null, null, null, null, null, null, null, null); + api = new API(configuration, null, null, null, null, null, null, null, null, null, null, null, null, null, null); } @Test diff --git a/src/test/java/com/iota/iri/service/validation/TransactionValidatorTest.java b/src/test/java/com/iota/iri/service/validation/TransactionValidatorTest.java index 99a39ea952..c2cb3c99fd 100644 --- a/src/test/java/com/iota/iri/service/validation/TransactionValidatorTest.java +++ b/src/test/java/com/iota/iri/service/validation/TransactionValidatorTest.java @@ -1,34 +1,27 @@ package com.iota.iri.service.validation; -import com.iota.iri.TransactionTestUtils; import com.iota.iri.conf.MainnetConfig; import com.iota.iri.conf.ProtocolConfig; - -import com.iota.iri.controllers.TipsViewModel; -import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.crypto.SpongeFactory; -import com.iota.iri.model.TransactionHash; import com.iota.iri.network.TransactionRequester; import com.iota.iri.service.snapshot.SnapshotProvider; - import com.iota.iri.service.snapshot.impl.SnapshotMockUtils; -import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.storage.Tangle; import com.iota.iri.storage.rocksDB.RocksDBPersistenceProvider; import com.iota.iri.utils.Converter; -import org.junit.*; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.Rule; +import org.junit.AfterClass; import org.junit.rules.TemporaryFolder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import static com.iota.iri.TransactionTestUtils.getTransactionHash; import static com.iota.iri.TransactionTestUtils.getTransactionTrits; -import static com.iota.iri.TransactionTestUtils.getTransactionTritsWithTrunkAndBranch; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -46,14 +39,17 @@ public class TransactionValidatorTest { @Mock private static SnapshotProvider snapshotProvider; + @Mock + private static TransactionRequester txRequester; + @BeforeClass public static void setUp() throws Exception { dbFolder.create(); logFolder.create(); tangle = new Tangle(); tangle.addPersistenceProvider( - new RocksDBPersistenceProvider( - dbFolder.getRoot().getAbsolutePath(), logFolder.getRoot().getAbsolutePath(),1000, Tangle.COLUMN_FAMILIES, Tangle.METADATA_COLUMN_FAMILY)); + new RocksDBPersistenceProvider( + dbFolder.getRoot().getAbsolutePath(), logFolder.getRoot().getAbsolutePath(),1000, Tangle.COLUMN_FAMILIES, Tangle.METADATA_COLUMN_FAMILY)); tangle.init(); } @@ -67,9 +63,8 @@ public static void tearDown() throws Exception { @Before public void setUpEach() { when(snapshotProvider.getInitialSnapshot()).thenReturn(SnapshotMockUtils.createSnapshot()); - TipsViewModel tipsViewModel = new TipsViewModel(); - TransactionRequester txRequester = new TransactionRequester(tangle, snapshotProvider); - txValidator = new TransactionValidator(tangle, snapshotProvider, tipsViewModel, txRequester, new MainnetConfig()); + txRequester = new TransactionRequester(tangle, snapshotProvider); + txValidator = new TransactionValidator(snapshotProvider, txRequester, new MainnetConfig()); txValidator.setMwm(false, MAINNET_MWM); } @@ -77,7 +72,7 @@ public void setUpEach() { public void testMinMwm() { ProtocolConfig protocolConfig = mock(ProtocolConfig.class); when(protocolConfig.getMwm()).thenReturn(5); - TransactionValidator transactionValidator = new TransactionValidator(null, null, null, null, protocolConfig); + TransactionValidator transactionValidator = new TransactionValidator(null, null, protocolConfig); assertEquals("Expected testnet minimum minWeightMagnitude", 13, transactionValidator.getMinWeightMagnitude()); } @@ -102,125 +97,4 @@ public void validateBytesWithNewCurl() { Converter.bytes(trits, 0, bytes, 0, trits.length); txValidator.validateBytes(bytes, txValidator.getMinWeightMagnitude(), SpongeFactory.create(SpongeFactory.Mode.CURLP81)); } - - @Test - public void verifyTxIsSolid() throws Exception { - TransactionViewModel tx = getTxWithBranchAndTrunk(); - assertTrue(txValidator.checkSolidity(tx.getHash())); - assertTrue(txValidator.checkSolidity(tx.getHash())); - } - - @Test - public void verifyTxIsNotSolid() throws Exception { - TransactionViewModel tx = getTxWithoutBranchAndTrunk(); - assertFalse(txValidator.checkSolidity(tx.getHash())); - assertFalse(txValidator.checkSolidity(tx.getHash())); - } - - @Test - public void addSolidTransactionWithoutErrors() { - byte[] trits = getTransactionTrits(); - Converter.copyTrits(0, trits, 0, trits.length); - txValidator.addSolidTransaction(TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits)); - } - - private TransactionViewModel getTxWithBranchAndTrunk() throws Exception { - TransactionViewModel tx, trunkTx, branchTx; - String trytes = "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999CFDEZBLZQYA9999999999999999999999999999999999999999999ZZWQHWD99C99999999C99999999CKWWDBWSCLMQULCTAAJGXDEMFJXPMGMAQIHDGHRBGEMUYNNCOK9YPHKEEFLFCZUSPMCJHAKLCIBQSGWAS999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"; - - byte[] trits = Converter.allocateTritsForTrytes(trytes.length()); - Converter.trits(trytes, trits, 0); - trunkTx = new TransactionViewModel(trits, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits)); - branchTx = new TransactionViewModel(trits, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits)); - - byte[] childTx = getTransactionTrits(); - System.arraycopy(trunkTx.getHash().trits(), 0, childTx, TransactionViewModel.TRUNK_TRANSACTION_TRINARY_OFFSET, TransactionViewModel.TRUNK_TRANSACTION_TRINARY_SIZE); - System.arraycopy(branchTx.getHash().trits(), 0, childTx, TransactionViewModel.BRANCH_TRANSACTION_TRINARY_OFFSET, TransactionViewModel.BRANCH_TRANSACTION_TRINARY_SIZE); - tx = new TransactionViewModel(childTx, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, childTx)); - - trunkTx.store(tangle, snapshotProvider.getInitialSnapshot()); - branchTx.store(tangle, snapshotProvider.getInitialSnapshot()); - tx.store(tangle, snapshotProvider.getInitialSnapshot()); - - return tx; - } - - @Test - public void testTransactionPropagation() throws Exception { - TransactionViewModel leftChildLeaf = TransactionTestUtils.createTransactionWithTrytes("CHILDTX"); - leftChildLeaf.updateSolid(true); - leftChildLeaf.store(tangle, snapshotProvider.getInitialSnapshot()); - - TransactionViewModel rightChildLeaf = TransactionTestUtils.createTransactionWithTrytes("CHILDTWOTX"); - rightChildLeaf.updateSolid(true); - rightChildLeaf.store(tangle, snapshotProvider.getInitialSnapshot()); - - TransactionViewModel parent = TransactionTestUtils.createTransactionWithTrunkAndBranch("PARENT", - leftChildLeaf.getHash(), rightChildLeaf.getHash()); - parent.updateSolid(false); - parent.store(tangle, snapshotProvider.getInitialSnapshot()); - - TransactionViewModel parentSibling = TransactionTestUtils.createTransactionWithTrytes("PARENTLEAF"); - parentSibling.updateSolid(true); - parentSibling.store(tangle, snapshotProvider.getInitialSnapshot()); - - TransactionViewModel grandParent = TransactionTestUtils.createTransactionWithTrunkAndBranch("GRANDPARENT", parent.getHash(), - parentSibling.getHash()); - grandParent.updateSolid(false); - grandParent.store(tangle, snapshotProvider.getInitialSnapshot()); - - txValidator.addSolidTransaction(leftChildLeaf.getHash()); - while (!txValidator.isNewSolidTxSetsEmpty()) { - txValidator.propagateSolidTransactions(); - } - - parent = TransactionViewModel.fromHash(tangle, parent.getHash()); - assertTrue("Parent tx was expected to be solid", parent.isSolid()); - grandParent = TransactionViewModel.fromHash(tangle, grandParent.getHash()); - assertTrue("Grandparent was expected to be solid", grandParent.isSolid()); - } - - @Test - public void testTransactionPropagationFailure() throws Exception { - TransactionViewModel leftChildLeaf = new TransactionViewModel(getTransactionTrits(), getTransactionHash()); - leftChildLeaf.updateSolid(true); - leftChildLeaf.store(tangle, snapshotProvider.getInitialSnapshot()); - - TransactionViewModel rightChildLeaf = new TransactionViewModel(getTransactionTrits(), getTransactionHash()); - rightChildLeaf.updateSolid(true); - rightChildLeaf.store(tangle, snapshotProvider.getInitialSnapshot()); - - TransactionViewModel parent = new TransactionViewModel(getTransactionTritsWithTrunkAndBranch(leftChildLeaf.getHash(), - rightChildLeaf.getHash()), getTransactionHash()); - parent.updateSolid(false); - parent.store(tangle, snapshotProvider.getInitialSnapshot()); - - TransactionViewModel parentSibling = new TransactionViewModel(getTransactionTrits(), getTransactionHash()); - parentSibling.updateSolid(false); - parentSibling.store(tangle, snapshotProvider.getInitialSnapshot()); - - TransactionViewModel grandParent = new TransactionViewModel(getTransactionTritsWithTrunkAndBranch(parent.getHash(), - parentSibling.getHash()), getTransactionHash()); - grandParent.updateSolid(false); - grandParent.store(tangle, snapshotProvider.getInitialSnapshot()); - - txValidator.addSolidTransaction(leftChildLeaf.getHash()); - while (!txValidator.isNewSolidTxSetsEmpty()) { - txValidator.propagateSolidTransactions(); - } - - parent = TransactionViewModel.fromHash(tangle, parent.getHash()); - assertTrue("Parent tx was expected to be solid", parent.isSolid()); - grandParent = TransactionViewModel.fromHash(tangle, grandParent.getHash()); - assertFalse("GrandParent tx was expected to be not solid", grandParent.isSolid()); - } - - private TransactionViewModel getTxWithoutBranchAndTrunk() throws Exception { - byte[] trits = getTransactionTrits(); - TransactionViewModel tx = new TransactionViewModel(trits, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits)); - - tx.store(tangle, snapshotProvider.getInitialSnapshot()); - - return tx; - } -} +} \ No newline at end of file diff --git a/src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java b/src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java new file mode 100644 index 0000000000..69fe1eb046 --- /dev/null +++ b/src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java @@ -0,0 +1,160 @@ +package com.iota.iri.service.validation.impl; + +import com.iota.iri.controllers.TipsViewModel; +import com.iota.iri.controllers.TransactionViewModel; +import com.iota.iri.crypto.SpongeFactory; +import com.iota.iri.model.TransactionHash; +import com.iota.iri.network.TransactionRequester; +import com.iota.iri.service.snapshot.SnapshotProvider; +import com.iota.iri.service.snapshot.impl.SnapshotMockUtils; +import com.iota.iri.storage.Tangle; +import com.iota.iri.storage.rocksDB.RocksDBPersistenceProvider; +import com.iota.iri.utils.Converter; +import org.junit.Rule; +import org.junit.BeforeClass; +import org.junit.Before; +import org.junit.AfterClass; +import org.junit.After; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import static com.iota.iri.TransactionTestUtils.getTransactionTrits; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +public class TransactionSolidifierImplTest { + private static final TemporaryFolder dbFolder = new TemporaryFolder(); + private static final TemporaryFolder logFolder = new TemporaryFolder(); + private static Tangle tangle; + + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private static SnapshotProvider snapshotProvider; + + @Mock + private static TransactionSolidifierImpl txSolidifier; + + @Mock + private static TipsViewModel tipsViewModel; + + @Mock + private static TransactionRequester txRequester; + + @BeforeClass + public static void setUp() throws Exception { + dbFolder.create(); + logFolder.create(); + tangle = new Tangle(); + tangle.addPersistenceProvider( + new RocksDBPersistenceProvider( + dbFolder.getRoot().getAbsolutePath(), logFolder.getRoot().getAbsolutePath(),1000, Tangle.COLUMN_FAMILIES, Tangle.METADATA_COLUMN_FAMILY)); + tangle.init(); + } + + @AfterClass + public static void tearDown() throws Exception { + tangle.shutdown(); + dbFolder.delete(); + logFolder.delete(); + } + + @Before + public void setUpEach() { + when(snapshotProvider.getInitialSnapshot()).thenReturn(SnapshotMockUtils.createSnapshot()); + txRequester = new TransactionRequester(tangle, snapshotProvider); + txSolidifier = new TransactionSolidifierImpl(tangle, snapshotProvider, txRequester, tipsViewModel); + txSolidifier.start(); + } + + @After + public void tearDownEach(){ + txSolidifier.shutdown(); + } + + + @Test + public void verifyTxIsSolid() throws Exception { + TransactionViewModel tx = getTxWithBranchAndTrunk(); + assertTrue("Expected transaction to be solid", txSolidifier.checkSolidity(tx.getHash())); + assertTrue("Expected transaction to be solid", txSolidifier.checkSolidity(tx.getHash())); + } + + @Test + public void verifyTxIsNotSolid() throws Exception { + TransactionViewModel tx = getTxWithoutBranchAndTrunk(); + assertFalse("Expected transaction to fail solidity check", txSolidifier.checkSolidity(tx.getHash())); + assertFalse("Expected transaction to fail solidity check", txSolidifier.checkSolidity(tx.getHash())); + } + + @Test + public void getSolidificationQueue() throws Exception { + TransactionViewModel mainTx = getTxWithBranchAndTrunk(); + for(int i = 0; i < 10; i++) { + TransactionViewModel tx = getTxWithBranchAndTrunk(); + txSolidifier.addToSolidificationQueue(tx.getHash()); + } + txSolidifier.addToSolidificationQueue(mainTx.getHash()); + assertTrue("Expected transaction to be present in the solidification queue", + txSolidifier.getSolidificationQueue().contains(mainTx.getHash())); + } + + @Test + public void verifyTransactionIsProcessedFully() throws Exception { + TransactionViewModel tx = getTxWithBranchAndTrunk(); + txSolidifier.addToSolidificationQueue(tx.getHash()); + + //Time to process through the steps + Thread.sleep(1000); + assertTrue("Expected transaction to be present in the broadcast queue", + txSolidifier.getBroadcastQueue().contains(tx)); + } + + + @Test + public void verifyInconsistentTransactionIsNotProcessedFully() throws Exception { + TransactionViewModel tx = getTxWithoutBranchAndTrunk(); + txSolidifier.addToSolidificationQueue(tx.getHash()); + + //Time to process through the steps + Thread.sleep(1000); + assertFalse("Expected transaction not to be present in the broadcast queue", + txSolidifier.getBroadcastQueue().contains(tx)); + } + + private TransactionViewModel getTxWithBranchAndTrunk() throws Exception { + TransactionViewModel tx, trunkTx, branchTx; + String trytes = "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999CFDEZBLZQYA9999999999999999999999999999999999999999999ZZWQHWD99C99999999C99999999CKWWDBWSCLMQULCTAAJGXDEMFJXPMGMAQIHDGHRBGEMUYNNCOK9YPHKEEFLFCZUSPMCJHAKLCIBQSGWAS999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"; + + byte[] trits = Converter.allocateTritsForTrytes(trytes.length()); + Converter.trits(trytes, trits, 0); + trunkTx = new TransactionViewModel(trits, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits)); + branchTx = new TransactionViewModel(trits, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits)); + + byte[] childTx = getTransactionTrits(); + System.arraycopy(trunkTx.getHash().trits(), 0, childTx, TransactionViewModel.TRUNK_TRANSACTION_TRINARY_OFFSET, TransactionViewModel.TRUNK_TRANSACTION_TRINARY_SIZE); + System.arraycopy(branchTx.getHash().trits(), 0, childTx, TransactionViewModel.BRANCH_TRANSACTION_TRINARY_OFFSET, TransactionViewModel.BRANCH_TRANSACTION_TRINARY_SIZE); + tx = new TransactionViewModel(childTx, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, childTx)); + + trunkTx.store(tangle, snapshotProvider.getInitialSnapshot()); + branchTx.store(tangle, snapshotProvider.getInitialSnapshot()); + tx.store(tangle, snapshotProvider.getInitialSnapshot()); + + return tx; + } + + private TransactionViewModel getTxWithoutBranchAndTrunk() throws Exception { + byte[] trits = getTransactionTrits(); + TransactionViewModel tx = new TransactionViewModel(trits, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits)); + + tx.store(tangle, snapshotProvider.getInitialSnapshot()); + + return tx; + } + +} \ No newline at end of file From 7ad63941db1af9d9eb688efb9b0b0b32a9b555a9 Mon Sep 17 00:00:00 2001 From: DyrellC Date: Thu, 26 Mar 2020 14:48:19 -0600 Subject: [PATCH 05/18] Remove tip field from solidify stage --- .../iri/network/pipeline/SolidifyStage.java | 22 +++++-------------- .../network/pipeline/SolidifyStageTest.java | 3 +-- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java b/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java index 85d4ee85ac..07bbaf0def 100644 --- a/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java +++ b/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java @@ -25,7 +25,6 @@ public class SolidifyStage implements Stage { private TransactionSolidifier txSolidifier; private TipsViewModel tipsViewModel; private Tangle tangle; - private TransactionViewModel tip; /** * Constructor for the {@link SolidifyStage}. @@ -71,27 +70,18 @@ public ProcessingContext process(ProcessingContext ctx){ } private ProcessingContext broadcastTip(ProcessingContext ctx, SolidifyPayload payload) throws Exception{ - if(tip == null) { - Hash tipHash = tipsViewModel.getRandomSolidTipHash(); + Hash tipHash = tipsViewModel.getRandomSolidTipHash(); - if (tipHash == null) { - ctx.setNextStage(TransactionProcessingPipeline.Stage.FINISH); - return ctx; - } - - tip = fromHash(tangle, tipHash); + if (tipHash == null) { + ctx.setNextStage(TransactionProcessingPipeline.Stage.FINISH); + return ctx; } + TransactionViewModel tip = fromHash(tangle, tipHash); + ctx.setNextStage(TransactionProcessingPipeline.Stage.BROADCAST); ctx.setPayload(new BroadcastPayload(payload.getOriginNeighbor(), tip)); - tip = null; return ctx; } - - @VisibleForTesting - void injectTip(TransactionViewModel tvm) throws Exception { - tip = tvm; - tip.updateSolid(true); - } } diff --git a/src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java b/src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java index 22f62411dc..be3582bf54 100644 --- a/src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java +++ b/src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java @@ -79,13 +79,12 @@ public void quickSetSolidTransactionIsBroadcast() throws Exception{ public void unsolidTransactionBroadcastsRandomSolidTip() throws Exception{ Mockito.when(tvm.isSolid()).thenReturn(false); Mockito.when(transactionSolidifier.quickSetSolid(tvm)).thenReturn(false); - TransactionViewModel tip = new TransactionViewModel(new Transaction(), tipHash); + Mockito.when(tipsViewModel.getRandomSolidTipHash()).thenReturn(tipHash); SolidifyStage solidifyStage = new SolidifyStage(transactionSolidifier, tipsViewModel, tangle); SolidifyPayload solidifyPayload = new SolidifyPayload(null, tvm); ProcessingContext ctx = new ProcessingContext(solidifyPayload); - solidifyStage.injectTip(tip); solidifyStage.process(ctx); Thread.sleep(100); From bff1915d73bc651053e6703159f168859bf0eec3 Mon Sep 17 00:00:00 2001 From: DyrellC Date: Thu, 26 Mar 2020 17:06:29 -0600 Subject: [PATCH 06/18] Move broadcast queue retreival to solidify stage --- .../iri/network/pipeline/SolidifyStage.java | 21 ++++++++++------ .../TransactionProcessingPipeline.java | 6 ----- .../TransactionProcessingPipelineImpl.java | 17 ------------- .../validation/TransactionSolidifier.java | 14 +++++------ .../impl/TransactionSolidifierImpl.java | 10 ++++---- .../impl/TransactionSolidifierImplTest.java | 24 +++++++++++++++++-- 6 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java b/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java index 07bbaf0def..4db01d6a7c 100644 --- a/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java +++ b/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java @@ -1,6 +1,5 @@ package com.iota.iri.network.pipeline; -import com.google.common.annotations.VisibleForTesting; import com.iota.iri.controllers.TipsViewModel; import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.model.Hash; @@ -55,6 +54,8 @@ public ProcessingContext process(ProcessingContext ctx){ TransactionViewModel tvm = payload.getTransaction(); if (tvm.isSolid() || txSolidifier.quickSetSolid(tvm)) { + // If the transaction is in the solidifier broadcast queue, remove it as it will be broadcast now + txSolidifier.clearFromBroadcastQueue(tvm); ctx.setNextStage(TransactionProcessingPipeline.Stage.BROADCAST); ctx.setPayload(new BroadcastPayload(payload.getOriginNeighbor(), payload.getTransaction())); return ctx; @@ -70,14 +71,20 @@ public ProcessingContext process(ProcessingContext ctx){ } private ProcessingContext broadcastTip(ProcessingContext ctx, SolidifyPayload payload) throws Exception{ - Hash tipHash = tipsViewModel.getRandomSolidTipHash(); + // First check if there is a transaction available to broadcast from the broadcast queue + TransactionViewModel tip = txSolidifier.getNextTxInBroadcastQueue(); - if (tipHash == null) { - ctx.setNextStage(TransactionProcessingPipeline.Stage.FINISH); - return ctx; - } + // If there is not a transaction available from the broadcast queue, instead try to send a solid tip + if (tip == null) { + Hash tipHash = tipsViewModel.getRandomSolidTipHash(); + + if (tipHash == null) { + ctx.setNextStage(TransactionProcessingPipeline.Stage.FINISH); + return ctx; + } - TransactionViewModel tip = fromHash(tangle, tipHash); + tip = fromHash(tangle, tipHash); + } ctx.setNextStage(TransactionProcessingPipeline.Stage.BROADCAST); ctx.setPayload(new BroadcastPayload(payload.getOriginNeighbor(), tip)); diff --git a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipeline.java b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipeline.java index 081deb810a..9c471a91c0 100644 --- a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipeline.java +++ b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipeline.java @@ -66,12 +66,6 @@ enum Stage { */ void process(byte[] txTrits); - /** - * Fetches a set of transactions from the {@link TransactionSolidifier} and submits - * the object into the {@link BroadcastStage} queue. - */ - void refillBroadcastQueue(); - /** * Shut downs the pipeline by shutting down all stages. */ diff --git a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java index 8f8c57d753..a847c1b35e 100644 --- a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java +++ b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java @@ -199,7 +199,6 @@ public BlockingQueue getValidationStageQueue() { public void process(Neighbor neighbor, ByteBuffer data) { try { preProcessStageQueue.put(new ProcessingContext(new PreProcessPayload(neighbor, data))); - refillBroadcastQueue(); } catch (InterruptedException e) { e.printStackTrace(); } @@ -214,22 +213,6 @@ public void process(byte[] txTrits) { hashAndValidate(new ProcessingContext(payload)); } - @Override - public void refillBroadcastQueue(){ - try{ - Iterator hashIterator = txSolidifier.getBroadcastQueue().iterator(); - Set toRemove = new LinkedHashSet<>(); - while(!Thread.currentThread().isInterrupted() && hashIterator.hasNext()){ - TransactionViewModel tx = hashIterator.next(); - broadcastStageQueue.put(new ProcessingContext(new BroadcastPayload(null, tx))); - toRemove.add(tx); - hashIterator.remove(); - } - txSolidifier.clearFromBroadcastQueue(toRemove); - } catch(InterruptedException e){ - log.info(e.getMessage()); - } - } /** * Sets up the given hashing stage {@link ProcessingContext} so that up on success, it will submit further to the diff --git a/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java b/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java index e79098318c..9dd8273cb4 100644 --- a/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java +++ b/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java @@ -6,8 +6,6 @@ import com.iota.iri.service.validation.impl.TransactionSolidifierImpl; import com.iota.iri.network.TransactionRequester; -import java.util.Set; - /** * Solidification tool. Transactions placed into the solidification queue will be checked for solidity. Any missing * reference transactions will be placed into the {@link TransactionRequester}. If a transaction is found to be solid @@ -42,16 +40,16 @@ public interface TransactionSolidifier { */ boolean addMilestoneToSolidificationQueue(Hash hash, int maxToProcess); /** - * Fetch a copy of the current transactionsToBroadcast set. - * @return A set of {@link TransactionViewModel} objects to be broadcast. + * Fetch the next transaction in the transactionsToBroadcast set. + * @return A {@link TransactionViewModel} object to be broadcast. */ - Set getBroadcastQueue(); + TransactionViewModel getNextTxInBroadcastQueue(); /** - * Remove any broadcasted transactions from the transactionsToBroadcast set - * @param transactionsBroadcasted A set of {@link TransactionViewModel} objects to remove from the set. + * Remove a broadcasted transaction from the transactionsToBroadcast set + * @param transactionsBroadcasted A {@link TransactionViewModel} object to remove from the set. */ - void clearFromBroadcastQueue(Set transactionsBroadcasted); + void clearFromBroadcastQueue(TransactionViewModel transactionsBroadcasted); /** * This method does the same as {@link #checkSolidity(Hash, int)} but defaults to an unlimited amount diff --git a/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java index 19ef32f7c8..7608f871e9 100644 --- a/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java +++ b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java @@ -137,18 +137,16 @@ public boolean addMilestoneToSolidificationQueue(Hash hash, int maxToProcess){ *{@inheritDoc} */ @Override - public Set getBroadcastQueue(){ - return new LinkedHashSet<>(transactionsToBroadcast); + public TransactionViewModel getNextTxInBroadcastQueue(){ + return transactionsToBroadcast.poll(); } /** *{@inheritDoc} */ @Override - public void clearFromBroadcastQueue(Set transactionsBroadcasted){ - for (TransactionViewModel tvm : transactionsBroadcasted) { - transactionsToBroadcast.remove(tvm); - } + public void clearFromBroadcastQueue(TransactionViewModel transaction){ + transactionsToBroadcast.remove(transaction); } diff --git a/src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java b/src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java index 69fe1eb046..0220fcaa82 100644 --- a/src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java +++ b/src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java @@ -21,6 +21,9 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import java.util.ArrayList; +import java.util.List; + import static com.iota.iri.TransactionTestUtils.getTransactionTrits; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -31,6 +34,9 @@ public class TransactionSolidifierImplTest { private static final TemporaryFolder logFolder = new TemporaryFolder(); private static Tangle tangle; + private List broadcastTransactions = new ArrayList<>(); + private TransactionViewModel transactionForBroadcast; + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @@ -111,8 +117,15 @@ public void verifyTransactionIsProcessedFully() throws Exception { //Time to process through the steps Thread.sleep(1000); + + while((transactionForBroadcast = txSolidifier.getNextTxInBroadcastQueue()) != null){ + broadcastTransactions.add(transactionForBroadcast); + } + assertTrue("Expected transaction to be present in the broadcast queue", - txSolidifier.getBroadcastQueue().contains(tx)); + broadcastTransactions.contains(tx)); + + broadcastTransactions.clear(); } @@ -123,8 +136,15 @@ public void verifyInconsistentTransactionIsNotProcessedFully() throws Exception //Time to process through the steps Thread.sleep(1000); + + while((transactionForBroadcast = txSolidifier.getNextTxInBroadcastQueue()) != null){ + broadcastTransactions.add(transactionForBroadcast); + } + assertFalse("Expected transaction not to be present in the broadcast queue", - txSolidifier.getBroadcastQueue().contains(tx)); + broadcastTransactions.contains(tx)); + + broadcastTransactions.clear(); } private TransactionViewModel getTxWithBranchAndTrunk() throws Exception { From 0f09a05c0c6253adfb28ef0d7ad6bf4eecdcdec2 Mon Sep 17 00:00:00 2001 From: DyrellC Date: Thu, 26 Mar 2020 17:27:31 -0600 Subject: [PATCH 07/18] Undo auto-formatting --- src/main/java/com/iota/iri/Iota.java | 26 +- src/main/java/com/iota/iri/service/API.java | 324 ++++++++++---------- 2 files changed, 175 insertions(+), 175 deletions(-) diff --git a/src/main/java/com/iota/iri/Iota.java b/src/main/java/com/iota/iri/Iota.java index 8b0f871ff4..420cc51c71 100644 --- a/src/main/java/com/iota/iri/Iota.java +++ b/src/main/java/com/iota/iri/Iota.java @@ -121,16 +121,16 @@ public class Iota { * */ public Iota(IotaConfig configuration, SpentAddressesProvider spentAddressesProvider, - SpentAddressesService spentAddressesService, SnapshotProvider snapshotProvider, - SnapshotService snapshotService, LocalSnapshotManager localSnapshotManager, - MilestoneService milestoneService, LatestMilestoneTracker latestMilestoneTracker, - LatestSolidMilestoneTracker latestSolidMilestoneTracker, SeenMilestonesRetriever seenMilestonesRetriever, - LedgerService ledgerService, TransactionPruner transactionPruner, MilestoneSolidifier milestoneSolidifier, - BundleValidator bundleValidator, Tangle tangle, TransactionValidator transactionValidator, - TransactionRequester transactionRequester, NeighborRouter neighborRouter, - TransactionProcessingPipeline transactionProcessingPipeline, TipsRequester tipsRequester, - TipsViewModel tipsViewModel, TipSelector tipsSelector, LocalSnapshotsPersistenceProvider localSnapshotsDb, - CacheManager cacheManager, TransactionSolidifier transactionSolidifier) { + SpentAddressesService spentAddressesService, SnapshotProvider snapshotProvider, + SnapshotService snapshotService, LocalSnapshotManager localSnapshotManager, + MilestoneService milestoneService, LatestMilestoneTracker latestMilestoneTracker, + LatestSolidMilestoneTracker latestSolidMilestoneTracker, SeenMilestonesRetriever seenMilestonesRetriever, + LedgerService ledgerService, TransactionPruner transactionPruner, MilestoneSolidifier milestoneSolidifier, + BundleValidator bundleValidator, Tangle tangle, TransactionValidator transactionValidator, + TransactionRequester transactionRequester, NeighborRouter neighborRouter, + TransactionProcessingPipeline transactionProcessingPipeline, TipsRequester tipsRequester, + TipsViewModel tipsViewModel, TipSelector tipsSelector, LocalSnapshotsPersistenceProvider localSnapshotsDb, + CacheManager cacheManager, TransactionSolidifier transactionSolidifier) { this.configuration = configuration; this.ledgerService = ledgerService; @@ -316,10 +316,10 @@ private void initializeTangle() { * @return A new Persistance provider */ private PersistenceProvider createRocksDbProvider(String path, String log, String configFile, int cacheSize, - Map> columnFamily, - Map.Entry> metadata) { + Map> columnFamily, + Map.Entry> metadata) { return new RocksDBPersistenceProvider( path, log, configFile, cacheSize, columnFamily, metadata); } -} \ No newline at end of file +} diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java index 1471cd990d..78a01d960e 100644 --- a/src/main/java/com/iota/iri/service/API.java +++ b/src/main/java/com/iota/iri/service/API.java @@ -67,33 +67,33 @@ public class API { private static final Logger log = LoggerFactory.getLogger(API.class); - + //region [CONSTANTS] /////////////////////////////////////////////////////////////////////////////// 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."; - + + "The subtangle has not been updated yet."; + private static final String OVER_MAX_ERROR_MESSAGE = "Could not complete request"; private static final String INVALID_PARAMS = "Invalid parameters"; private static final char ZERO_LENGTH_ALLOWED = 'Y'; private static final char ZERO_LENGTH_NOT_ALLOWED = 'N'; - + private static final int HASH_SIZE = 81; private static final int TRYTES_SIZE = 2673; private static final long MAX_TIMESTAMP_VALUE = (long) (Math.pow(3, 27) - 1) / 2; // max positive 27-trits value //endregion //////////////////////////////////////////////////////////////////////////////////////////////////////// - + private static int counterGetTxToApprove = 0; private static long ellapsedTime_getTxToApprove = 0L; private static int counter_PoW = 0; private static long ellapsedTime_PoW = 0L; - + //region [CONSTRUCTOR_FIELDS] /////////////////////////////////////////////////////////////////////////////// private final IotaConfig configuration; @@ -111,13 +111,13 @@ public class API { private final TransactionValidator transactionValidator; private final TransactionSolidifier transactionSolidifier; private final LatestMilestoneTracker latestMilestoneTracker; - + private final int maxFindTxs; private final int maxRequestList; private final int maxGetTrytes; private final String[] features; - + //endregion //////////////////////////////////////////////////////////////////////////////////////////////////////// private final Gson gson = new GsonBuilder().create(); @@ -136,9 +136,9 @@ public class API { /** * Starts loading the IOTA API, parameters do not have to be initialized. - * + * * @param configuration Holds IRI configuration parameters. - * @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} * @param transactionRequester Service where transactions get requested * @param spentAddressesService Service to check if addresses are spent @@ -156,14 +156,14 @@ public class API { * */ public API(IotaConfig configuration, IXI ixi, TransactionRequester transactionRequester, - SpentAddressesService spentAddressesService, Tangle tangle, BundleValidator bundleValidator, - SnapshotProvider snapshotProvider, LedgerService ledgerService, NeighborRouter neighborRouter, - TipSelector tipsSelector, TipsViewModel tipsViewModel, TransactionValidator transactionValidator, - LatestMilestoneTracker latestMilestoneTracker, TransactionProcessingPipeline txPipeline, - TransactionSolidifier transactionSolidifier) { + SpentAddressesService spentAddressesService, Tangle tangle, BundleValidator bundleValidator, + SnapshotProvider snapshotProvider, LedgerService ledgerService, NeighborRouter neighborRouter, + TipSelector tipsSelector, TipsViewModel tipsViewModel, TransactionValidator transactionValidator, + LatestMilestoneTracker latestMilestoneTracker, TransactionProcessingPipeline txPipeline, + TransactionSolidifier transactionSolidifier) { this.configuration = configuration; this.ixi = ixi; - + this.transactionRequester = transactionRequester; this.spentAddressesService = spentAddressesService; this.tangle = tangle; @@ -177,13 +177,13 @@ public API(IotaConfig configuration, IXI ixi, TransactionRequester transactionRe this.transactionValidator = transactionValidator; this.transactionSolidifier = transactionSolidifier; this.latestMilestoneTracker = latestMilestoneTracker; - + maxFindTxs = configuration.getMaxFindTransactions(); maxRequestList = configuration.getMaxRequestsList(); maxGetTrytes = configuration.getMaxGetTrytes(); features = Feature.calculateFeatureNames(configuration); - + commandRoute = new HashMap<>(); commandRoute.put(ApiCommand.ADD_NEIGHBORS, addNeighbors()); commandRoute.put(ApiCommand.ATTACH_TO_TANGLE, attachToTangle()); @@ -208,7 +208,7 @@ public API(IotaConfig configuration, IXI ixi, TransactionRequester transactionRe /** * Initializes the API for usage. * Will initialize and start the supplied {@link RestConnector} - * + * * @param connector THe connector we use to handle API requests */ public void init(RestConnector connector){ @@ -216,7 +216,7 @@ public void init(RestConnector connector){ connector.init(this::process); connector.start(); } - + /** * Handles an API request body. * Its returned {@link AbstractResponse} is created using the following logic @@ -479,20 +479,20 @@ private AbstractResponse addNeighborsStatement(List uris) { } return AddedNeighborsResponse.create(numberOfAddedNeighbors); } - + /** - * Temporarily removes a list of neighbors from your node. - * The added neighbors will be added again after relaunching IRI. - * Remove the neighbors from your config file or make sure you don't supply them in the -n command line option if you want to keep them removed after restart. - * - * The URI (Unique Resource Identification) for removing neighbors is: - * udp://IPADDRESS:PORT - * - * Returns an {@link com.iota.iri.service.dto.ErrorResponse} if the URI scheme is wrong - * - * @param uris The URIs of the neighbors we want to remove. - * @return {@link com.iota.iri.service.dto.RemoveNeighborsResponse} - **/ + * Temporarily removes a list of neighbors from your node. + * The added neighbors will be added again after relaunching IRI. + * Remove the neighbors from your config file or make sure you don't supply them in the -n command line option if you want to keep them removed after restart. + * + * The URI (Unique Resource Identification) for removing neighbors is: + * udp://IPADDRESS:PORT + * + * Returns an {@link com.iota.iri.service.dto.ErrorResponse} if the URI scheme is wrong + * + * @param uris The URIs of the neighbors we want to remove. + * @return {@link com.iota.iri.service.dto.RemoveNeighborsResponse} + **/ @Document(name="removeNeighbors") private AbstractResponse removeNeighborsStatement(List uris) { int numberOfRemovedNeighbors = 0; @@ -521,13 +521,13 @@ private AbstractResponse removeNeighborsStatement(List uris) { } /** - * Returns raw transaction data (trytes) of a specific transaction. - * These trytes can then be converted into the actual transaction object. - * See utility and {@link Transaction} functions in an IOTA library for more details. - * - * @param hashes The transaction hashes you want to get trytes from. - * @return {@link com.iota.iri.service.dto.GetTrytesResponse} - **/ + * Returns raw transaction data (trytes) of a specific transaction. + * These trytes can then be converted into the actual transaction object. + * See utility and {@link Transaction} functions in an IOTA library for more details. + * + * @param hashes The transaction hashes you want to get trytes from. + * @return {@link com.iota.iri.service.dto.GetTrytesResponse} + **/ @Document(name="getTrytes") private synchronized AbstractResponse getTrytesStatement(List hashes) throws Exception { final List elements = new LinkedList<>(); @@ -579,17 +579,17 @@ private static void incEllapsedTimeGetTxToApprove(long ellapsedTime) { } /** - * Tip selection which returns trunkTransaction and branchTransaction. - * The input value depth determines how many milestones to go back for finding the transactions to approve. - * The higher your depth value, the more work you have to do as you are confirming more transactions. - * If the depth is too large (usually above 15, it depends on the node's configuration) an error will be returned. - * The reference is an optional hash of a transaction you want to approve. - * If it can't be found at the specified depth then an error will be returned. - * - * @param depth Number of bundles to go back to determine the transactions for approval. - * @param reference Hash of transaction to start random-walk from, used to make sure the tips returned reference a given transaction in their past. - * @return {@link com.iota.iri.service.dto.GetTransactionsToApproveResponse} - **/ + * Tip selection which returns trunkTransaction and branchTransaction. + * The input value depth determines how many milestones to go back for finding the transactions to approve. + * The higher your depth value, the more work you have to do as you are confirming more transactions. + * If the depth is too large (usually above 15, it depends on the node's configuration) an error will be returned. + * The reference is an optional hash of a transaction you want to approve. + * If it can't be found at the specified depth then an error will be returned. + * + * @param depth Number of bundles to go back to determine the transactions for approval. + * @param reference Hash of transaction to start random-walk from, used to make sure the tips returned reference a given transaction in their past. + * @return {@link com.iota.iri.service.dto.GetTransactionsToApproveResponse} + **/ @Document(name="getTransactionsToApprove") private synchronized AbstractResponse getTransactionsToApproveStatement(int depth, Optional reference) { if (depth < 0 || depth > configuration.getMaxDepth()) { @@ -641,7 +641,7 @@ List getTransactionToApproveTips(int depth, Optional reference) thro /** *

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

*

@@ -662,10 +662,10 @@ private void gatherStatisticsOnTipSelection() { } /** - * Returns all tips currently known by this node. - * - * @return {@link com.iota.iri.service.dto.GetTipsResponse} - **/ + * Returns all tips currently known by this node. + * + * @return {@link com.iota.iri.service.dto.GetTipsResponse} + **/ @Document(name="getTips") private synchronized AbstractResponse getTipsStatement() throws Exception { return GetTipsResponse.create(tipsViewModel.getTips() @@ -675,14 +675,14 @@ private synchronized AbstractResponse getTipsStatement() throws Exception { } /** - * Stores transactions in the local storage. - * 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 Transaction data to be stored. - * @return {@link com.iota.iri.service.dto.AbstractResponse.Emptyness} - * @throws Exception When storing or updating a transaction fails. - **/ + * Stores transactions in the local storage. + * 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 Transaction data to be stored. + * @return {@link com.iota.iri.service.dto.AbstractResponse.Emptyness} + * @throws Exception When storing or updating a transaction fails. + **/ @Document(name="storeTransactions") public AbstractResponse storeTransactionsStatement(List trytes) throws Exception { final List elements = convertTrytes(trytes); @@ -699,10 +699,10 @@ public AbstractResponse storeTransactionsStatement(List trytes) throws E } /** - * Interrupts and completely aborts the attachToTangle process. - * - * @return {@link com.iota.iri.service.dto.AbstractResponse.Emptyness} - **/ + * Interrupts and completely aborts the attachToTangle process. + * + * @return {@link com.iota.iri.service.dto.AbstractResponse.Emptyness} + **/ @Document(name="interruptAttachingToTangle") private AbstractResponse interruptAttachingToTangleStatement(){ pearlDiver.cancel(); @@ -710,16 +710,16 @@ private AbstractResponse interruptAttachingToTangleStatement(){ } /** - * Returns information about this node. - * - * @return {@link com.iota.iri.service.dto.GetNodeInfoResponse} - * @throws Exception When we cant find the first milestone in the database - **/ + * Returns information about this node. + * + * @return {@link com.iota.iri.service.dto.GetNodeInfoResponse} + * @throws Exception When we cant find the first milestone in the database + **/ @Document(name="getNodeInfo") private AbstractResponse getNodeInfoStatement() throws Exception{ String name = configuration.isTestnet() ? IRI.TESTNET_NAME : IRI.MAINNET_NAME; MilestoneViewModel milestone = MilestoneViewModel.first(tangle); - + return GetNodeInfoResponse.create( name, IotaUtils.getIriVersion(), @@ -731,13 +731,13 @@ private AbstractResponse getNodeInfoStatement() throws Exception{ Runtime.getRuntime().totalMemory(), latestMilestoneTracker.getLatestMilestoneHash(), latestMilestoneTracker.getLatestMilestoneIndex(), - + snapshotProvider.getLatestSnapshot().getHash(), snapshotProvider.getLatestSnapshot().getIndex(), - + milestone != null ? milestone.index() : -1, snapshotProvider.getLatestSnapshot().getInitialIndex(), - + neighborRouter.getConnectedNeighbors().size(), txPipeline.getBroadcastStageQueue().size(), System.currentTimeMillis(), @@ -849,20 +849,20 @@ private boolean exhaustiveSearchWithinIndex( } /** - *

- * Find transactions that contain the given values in their transaction fields. - * 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 transaction fields returns transactions hashes at the intersection of those values. - * - * @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 - * or the total transactions to find exceeds {@link APIConfig#getMaxFindTransactions()}. - **/ + *

+ * Find transactions that contain the given values in their transaction fields. + * 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 transaction fields returns transactions hashes at the intersection of those values. + * + * @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 + * or the total transactions to find exceeds {@link APIConfig#getMaxFindTransactions()}. + **/ @Document(name="findTransactions") private synchronized AbstractResponse findTransactionsStatement(final Map request) throws Exception { @@ -875,7 +875,7 @@ private synchronized AbstractResponse findTransactionsStatement(final Maptag is a {@link Hash#NULL_HASH}. */ private String padTag(String tag) throws ValidationException { @@ -999,13 +999,13 @@ 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 - * @return {@link com.iota.iri.service.dto.AbstractResponse.Emptyness} - **/ + * 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 + * @return {@link com.iota.iri.service.dto.AbstractResponse.Emptyness} + **/ @Document(name="broadcastTransactions") public AbstractResponse broadcastTransactionsStatement(List trytes) { for (final String tryte : trytes) { @@ -1018,23 +1018,23 @@ public AbstractResponse broadcastTransactionsStatement(List trytes) { /** - *

- * Calculates the confirmed balance, as viewed by the specified tips. - * If the tips parameter is missing, the returned balance is correct as of the latest confirmed 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. - *

- * - * @param addresses Address for which to get the balance (do not include the checksum) - * @param tips The optional tips to find the balance through. - * @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 - **/ + *

+ * Calculates the confirmed balance, as viewed by the specified tips. + * If the tips parameter is missing, the returned balance is correct as of the latest confirmed 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. + *

+ * + * @param addresses Address for which to get the balance (do not include the checksum) + * @param tips The optional tips to find the balance through. + * @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 + **/ @Document(name="getBalances") - private AbstractResponse getBalancesStatement(List addresses, + private AbstractResponse getBalancesStatement(List addresses, List tips, int threshold) throws Exception { @@ -1136,37 +1136,37 @@ 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 obtainable by the getTransactionsToApprove API call. - *

- *

- * 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: - *

    - *
  • trunkTransaction
  • - *
  • branchTransaction
  • - *
  • nonce
  • - *
- *

- * These are valid trytes which are then accepted by the network. - * @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. - * 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. - * @return The list of transactions in trytes, ready to be broadcast to the network. - **/ + *

+ * 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 obtainable by the getTransactionsToApprove API call. + *

+ *

+ * 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: + *

    + *
  • trunkTransaction
  • + *
  • branchTransaction
  • + *
  • nonce
  • + *
+ *

+ * These are valid trytes which are then accepted by the network. + * @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. + * 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. + * @return The list of transactions in trytes, ready to be broadcast to the network. + **/ @Document(name="attachToTangle", returnParam="trytes") public synchronized List attachToTangleStatement(Hash trunkTransaction, Hash branchTransaction, int minWeightMagnitude, List trytes) { @@ -1447,7 +1447,7 @@ private synchronized AbstractResponse storeMessageStatement(String address, Stri return AbstractResponse.createEmptyResponse(); } - + // // FUNCTIONAL COMMAND ROUTES // @@ -1494,10 +1494,10 @@ private Function, AbstractResponse> getBalances() { return request -> { final List addresses = getParameterAsList(request,"addresses", HASH_SIZE); final List tips = request.containsKey("tips") ? - getParameterAsList(request,"tips", HASH_SIZE): - null; + getParameterAsList(request,"tips", HASH_SIZE): + null; final int threshold = getParameterAsInt(request, "threshold"); - + try { return getBalancesStatement(addresses, tips, threshold); } catch (Exception e) { @@ -1534,7 +1534,7 @@ private Function, AbstractResponse> getNodeInfo() { } }; } - + private Function, AbstractResponse> getNodeAPIConfiguration() { return request -> getNodeAPIConfigurationStatement(); } @@ -1552,8 +1552,8 @@ private Function, AbstractResponse> getTips() { private Function, AbstractResponse> getTransactionsToApprove() { return request -> { Optional reference = request.containsKey("reference") ? - Optional.of(HashFactory.TRANSACTION.create(getParameterAsStringAndValidate(request,"reference", HASH_SIZE))) - : Optional.empty(); + Optional.of(HashFactory.TRANSACTION.create(getParameterAsStringAndValidate(request,"reference", HASH_SIZE))) + : Optional.empty(); int depth = getParameterAsInt(request, "depth"); return getTransactionsToApproveStatement(depth, reference); @@ -1606,7 +1606,7 @@ private Function, AbstractResponse> getMissingTransactions() } }; } - + private Function, AbstractResponse> checkConsistency() { return request -> { if (!isNodeSynchronized()) { @@ -1619,7 +1619,7 @@ private Function, AbstractResponse> checkConsistency() { throw new IllegalStateException(e); } }; - } + } private Function, AbstractResponse> wereAddressesSpentFrom() { return request -> { final List addresses = getParameterAsList(request,"addresses", HASH_SIZE); @@ -1644,4 +1644,4 @@ private List convertTrytes(List trytes) { return elements; } -} \ No newline at end of file +} From faeabc3ce17d477fae8883f462e8e4a1fa15beb1 Mon Sep 17 00:00:00 2001 From: DyrellC Date: Thu, 26 Mar 2020 17:33:40 -0600 Subject: [PATCH 08/18] More autoformatting --- .../iota/iri/MainInjectionConfiguration.java | 34 +++++++++---------- .../NetworkInjectionConfiguration.java | 8 ++--- .../TransactionProcessingPipelineImpl.java | 31 +++++++++++++---- 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/iota/iri/MainInjectionConfiguration.java b/src/main/java/com/iota/iri/MainInjectionConfiguration.java index ed464ea40e..221256930d 100644 --- a/src/main/java/com/iota/iri/MainInjectionConfiguration.java +++ b/src/main/java/com/iota/iri/MainInjectionConfiguration.java @@ -103,8 +103,8 @@ LatestMilestoneTracker provideLatestMilestoneTracker(Tangle tangle, SnapshotProv @Singleton @Provides LatestSolidMilestoneTracker provideLatestSolidMilestoneTracker(Tangle tangle, SnapshotProvider snapshotProvider, - MilestoneService milestoneService, LedgerService ledgerService, - LatestMilestoneTracker latestMilestoneTracker, TransactionRequester transactionRequester) { + MilestoneService milestoneService, LedgerService ledgerService, + LatestMilestoneTracker latestMilestoneTracker, TransactionRequester transactionRequester) { return new LatestSolidMilestoneTrackerImpl(tangle, snapshotProvider, milestoneService, ledgerService, latestMilestoneTracker, transactionRequester, configuration); } @@ -171,16 +171,16 @@ TipSelector provideTipSelector(Tangle tangle, SnapshotProvider snapshotProvider, @Singleton @Provides Iota provideIota(SpentAddressesProvider spentAddressesProvider, SpentAddressesService spentAddressesService, - SnapshotProvider snapshotProvider, SnapshotService snapshotService, - @Nullable LocalSnapshotManager localSnapshotManager, MilestoneService milestoneService, - LatestMilestoneTracker latestMilestoneTracker, LatestSolidMilestoneTracker latestSolidMilestoneTracker, - SeenMilestonesRetriever seenMilestonesRetriever, LedgerService ledgerService, - @Nullable TransactionPruner transactionPruner, MilestoneSolidifier milestoneSolidifier, - BundleValidator bundleValidator, Tangle tangle, TransactionValidator transactionValidator, - TransactionRequester transactionRequester, NeighborRouter neighborRouter, - TransactionProcessingPipeline transactionProcessingPipeline, TipsRequester tipsRequester, - TipsViewModel tipsViewModel, TipSelector tipsSelector, LocalSnapshotsPersistenceProvider localSnapshotsDb, - CacheManager cacheManager, TransactionSolidifier transactionSolidifier) { + SnapshotProvider snapshotProvider, SnapshotService snapshotService, + @Nullable LocalSnapshotManager localSnapshotManager, MilestoneService milestoneService, + LatestMilestoneTracker latestMilestoneTracker, LatestSolidMilestoneTracker latestSolidMilestoneTracker, + SeenMilestonesRetriever seenMilestonesRetriever, LedgerService ledgerService, + @Nullable TransactionPruner transactionPruner, MilestoneSolidifier milestoneSolidifier, + BundleValidator bundleValidator, Tangle tangle, TransactionValidator transactionValidator, + TransactionRequester transactionRequester, NeighborRouter neighborRouter, + TransactionProcessingPipeline transactionProcessingPipeline, TipsRequester tipsRequester, + TipsViewModel tipsViewModel, TipSelector tipsSelector, LocalSnapshotsPersistenceProvider localSnapshotsDb, + CacheManager cacheManager, TransactionSolidifier transactionSolidifier) { return new Iota(configuration, spentAddressesProvider, spentAddressesService, snapshotProvider, snapshotService, localSnapshotManager, milestoneService, latestMilestoneTracker, latestSolidMilestoneTracker, seenMilestonesRetriever, ledgerService, transactionPruner, milestoneSolidifier, bundleValidator, tangle, @@ -197,10 +197,10 @@ IXI provideIxi(Iota iota) { @Singleton @Provides API provideApi(IXI ixi, TransactionRequester transactionRequester, - SpentAddressesService spentAddressesService, Tangle tangle, BundleValidator bundleValidator, - SnapshotProvider snapshotProvider, LedgerService ledgerService, NeighborRouter neighborRouter, TipSelector tipsSelector, - TipsViewModel tipsViewModel, TransactionValidator transactionValidator, - LatestMilestoneTracker latestMilestoneTracker, TransactionProcessingPipeline txPipeline, TransactionSolidifier transactionSolidifier) { + SpentAddressesService spentAddressesService, Tangle tangle, BundleValidator bundleValidator, + SnapshotProvider snapshotProvider, LedgerService ledgerService, NeighborRouter neighborRouter, TipSelector tipsSelector, + TipsViewModel tipsViewModel, TransactionValidator transactionValidator, + LatestMilestoneTracker latestMilestoneTracker, TransactionProcessingPipeline txPipeline, TransactionSolidifier transactionSolidifier) { return new API(configuration, ixi, transactionRequester, spentAddressesService, tangle, bundleValidator, snapshotProvider, ledgerService, neighborRouter, tipsSelector, tipsViewModel, transactionValidator, latestMilestoneTracker, txPipeline, transactionSolidifier); } @@ -222,4 +222,4 @@ protected void configure() { bind(BundleValidator.class).asEagerSingleton(); bind(TipsViewModel.class).asEagerSingleton(); } -} \ No newline at end of file +} diff --git a/src/main/java/com/iota/iri/network/NetworkInjectionConfiguration.java b/src/main/java/com/iota/iri/network/NetworkInjectionConfiguration.java index 5d0f82d839..d4caa618e5 100644 --- a/src/main/java/com/iota/iri/network/NetworkInjectionConfiguration.java +++ b/src/main/java/com/iota/iri/network/NetworkInjectionConfiguration.java @@ -45,9 +45,9 @@ TipsRequester provideTipsRequester(NeighborRouter neighborRouter, Tangle tangle, @Singleton @Provides TransactionProcessingPipeline provideTransactionProcessingPipeline(NeighborRouter neighborRouter, - TransactionValidator txValidator, Tangle tangle, SnapshotProvider snapshotProvider, - TipsViewModel tipsViewModel, LatestMilestoneTracker latestMilestoneTracker, - TransactionRequester transactionRequester, TransactionSolidifier transactionSolidifier) { + TransactionValidator txValidator, Tangle tangle, SnapshotProvider snapshotProvider, + TipsViewModel tipsViewModel, LatestMilestoneTracker latestMilestoneTracker, + TransactionRequester transactionRequester, TransactionSolidifier transactionSolidifier) { return new TransactionProcessingPipelineImpl(neighborRouter, configuration, txValidator, tangle, snapshotProvider, tipsViewModel, latestMilestoneTracker, transactionRequester, transactionSolidifier); } @@ -58,4 +58,4 @@ NeighborRouter provideNeighborRouter(TransactionRequester transactionRequester, return new NeighborRouterImpl(configuration, configuration, transactionRequester, transactionProcessingPipeline); } -} \ No newline at end of file +} diff --git a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java index a847c1b35e..d736b4c49d 100644 --- a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java +++ b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java @@ -97,9 +97,9 @@ public class TransactionProcessingPipelineImpl implements TransactionProcessingP * reply stage */ public TransactionProcessingPipelineImpl(NeighborRouter neighborRouter, NodeConfig config, - TransactionValidator txValidator, Tangle tangle, SnapshotProvider snapshotProvider, - TipsViewModel tipsViewModel, LatestMilestoneTracker latestMilestoneTracker, - TransactionRequester transactionRequester, TransactionSolidifier txSolidifier) { + TransactionValidator txValidator, Tangle tangle, SnapshotProvider snapshotProvider, + TipsViewModel tipsViewModel, LatestMilestoneTracker latestMilestoneTracker, + TransactionRequester transactionRequester, TransactionSolidifier txSolidifier) { FIFOCache recentlySeenBytesCache = new FIFOCache<>(config.getCacheSizeBytes()); this.preProcessStage = new PreProcessStage(recentlySeenBytesCache); this.replyStage = new ReplyStage(neighborRouter, config, tangle, tipsViewModel, latestMilestoneTracker, @@ -126,13 +126,13 @@ public void start() { /** * Adds the given stage to the processing pipeline. - * + * * @param name the name of the stage * @param queue the queue from which contexts are taken to process within the stage * @param stage the stage with the processing logic */ private void addStage(String name, BlockingQueue queue, - com.iota.iri.network.pipeline.Stage stage) { + com.iota.iri.network.pipeline.Stage stage) { stagesThreadPool.submit(new Thread(() -> { try { while (!Thread.currentThread().isInterrupted()) { @@ -199,6 +199,7 @@ public BlockingQueue getValidationStageQueue() { public void process(Neighbor neighbor, ByteBuffer data) { try { preProcessStageQueue.put(new ProcessingContext(new PreProcessPayload(neighbor, data))); + refillBroadcastQueue(); } catch (InterruptedException e) { e.printStackTrace(); } @@ -213,11 +214,27 @@ public void process(byte[] txTrits) { hashAndValidate(new ProcessingContext(payload)); } + @Override + public void refillBroadcastQueue(){ + try{ + Iterator hashIterator = txSolidifier.getBroadcastQueue().iterator(); + Set toRemove = new LinkedHashSet<>(); + while(!Thread.currentThread().isInterrupted() && hashIterator.hasNext()){ + TransactionViewModel tx = hashIterator.next(); + broadcastStageQueue.put(new ProcessingContext(new BroadcastPayload(null, tx))); + toRemove.add(tx); + hashIterator.remove(); + } + txSolidifier.clearFromBroadcastQueue(toRemove); + } catch(InterruptedException e){ + log.info(e.getMessage()); + } + } /** * Sets up the given hashing stage {@link ProcessingContext} so that up on success, it will submit further to the * validation stage. - * + * * @param ctx the hashing stage {@link ProcessingContext} */ private void hashAndValidate(ProcessingContext ctx) { @@ -276,4 +293,4 @@ public void setHashingStage(HashingStage hashingStage) { public void setSolidifyStage(SolidifyStage solidifyStage){ this.solidifyStage = solidifyStage; } -} \ No newline at end of file +} From b96d70b669e20afb99e4094d05757c04acaecea1 Mon Sep 17 00:00:00 2001 From: DyrellC Date: Thu, 26 Mar 2020 17:38:17 -0600 Subject: [PATCH 09/18] Re-remove refillBroadcast --- .../TransactionProcessingPipelineImpl.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java index d736b4c49d..d1e1e88b6d 100644 --- a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java +++ b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java @@ -199,7 +199,6 @@ public BlockingQueue getValidationStageQueue() { public void process(Neighbor neighbor, ByteBuffer data) { try { preProcessStageQueue.put(new ProcessingContext(new PreProcessPayload(neighbor, data))); - refillBroadcastQueue(); } catch (InterruptedException e) { e.printStackTrace(); } @@ -214,23 +213,6 @@ public void process(byte[] txTrits) { hashAndValidate(new ProcessingContext(payload)); } - @Override - public void refillBroadcastQueue(){ - try{ - Iterator hashIterator = txSolidifier.getBroadcastQueue().iterator(); - Set toRemove = new LinkedHashSet<>(); - while(!Thread.currentThread().isInterrupted() && hashIterator.hasNext()){ - TransactionViewModel tx = hashIterator.next(); - broadcastStageQueue.put(new ProcessingContext(new BroadcastPayload(null, tx))); - toRemove.add(tx); - hashIterator.remove(); - } - txSolidifier.clearFromBroadcastQueue(toRemove); - } catch(InterruptedException e){ - log.info(e.getMessage()); - } - } - /** * Sets up the given hashing stage {@link ProcessingContext} so that up on success, it will submit further to the * validation stage. From a21e96922d04a5b469b5b3ee1e469b2083fe4415 Mon Sep 17 00:00:00 2001 From: DyrellC Date: Thu, 26 Mar 2020 18:10:11 -0600 Subject: [PATCH 10/18] Move propagation logic to inner class --- .../validation/TransactionSolidifier.java | 7 -- .../impl/TransactionSolidifierImpl.java | 105 ++++++++++-------- 2 files changed, 59 insertions(+), 53 deletions(-) diff --git a/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java b/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java index 9dd8273cb4..b7b9f16d20 100644 --- a/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java +++ b/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java @@ -114,11 +114,4 @@ public interface TransactionSolidifier { * @throws Exception */ boolean quickSetSolid(TransactionViewModel transactionViewModel) throws Exception; - - /** - * Add to the propagation queue where it will be processed to help solidify approving transactions faster - * @param hash The transaction hash to be removed - * @throws Exception - */ - void addToPropagationQueue(Hash hash) throws Exception; } \ No newline at end of file diff --git a/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java index 7608f871e9..fea1aa17ab 100644 --- a/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java +++ b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java @@ -51,11 +51,6 @@ public class TransactionSolidifierImpl implements TransactionSolidifier { */ private BlockingQueue transactionsToSolidify = new ArrayBlockingQueue<>(MAX_SIZE); - /** - * A queue for processing transactions with the {@link #propagateSolidTransactions()} call. This will check - * approving transactions with {@link #quickSetSolid(TransactionViewModel)}. - */ - private BlockingQueue solidTransactions = new ArrayBlockingQueue<>(MAX_SIZE); /** * A set of transactions that will be called by the {@link TransactionProcessingPipeline} to be broadcast to @@ -65,6 +60,8 @@ public class TransactionSolidifierImpl implements TransactionSolidifier { private TipsViewModel tipsViewModel; + private TransactionPropagator transactionPropagator; + /** * Constructor for the solidifier. * @param tangle The DB reference @@ -77,6 +74,7 @@ public TransactionSolidifierImpl(Tangle tangle, SnapshotProvider snapshotProvide this.snapshotProvider = snapshotProvider; this.transactionRequester = transactionRequester; this.tipsViewModel = tipsViewModel; + this.transactionPropagator = new TransactionPropagator(); } /** @@ -122,7 +120,7 @@ public boolean addMilestoneToSolidificationQueue(Hash hash, int maxToProcess){ try{ TransactionViewModel tx = fromHash(tangle, hash); if(tx.isSolid()){ - addToPropagationQueue(hash); + transactionPropagator.addToPropagationQueue(hash); return true; } addToSolidificationQueue(hash); @@ -163,7 +161,7 @@ private void processTransactionsToSolidify(){ log.info(e.getMessage()); } } - propagateSolidTransactions(); + transactionPropagator.propagateSolidTransactions(); } /** @@ -242,7 +240,7 @@ private void updateTransactions(Set hashes) { tvm.update(tangle, snapshotProvider.getInitialSnapshot(), "solid|height"); } addToBroadcastQueue(tvm); - addToPropagationQueue(tvm.getHash()); + transactionPropagator.addToPropagationQueue(tvm.getHash()); } catch (Exception e) { log.info(e.getMessage()); } @@ -256,7 +254,7 @@ private boolean isUnsolidWithoutEntryPoint(TransactionViewModel transaction, Has if(!transaction.isSolid() && !snapshotProvider.getInitialSnapshot().hasSolidEntryPoint(hashPointer)){ return true; } - addToPropagationQueue(hashPointer); + transactionPropagator.addToPropagationQueue(hashPointer); return false; } @@ -291,19 +289,10 @@ public void updateStatus(TransactionViewModel transactionViewModel) throws Excep if(quickSetSolid(transactionViewModel)) { transactionViewModel.update(tangle, snapshotProvider.getInitialSnapshot(), "solid|height"); tipsViewModel.setSolid(transactionViewModel.getHash()); - addToPropagationQueue(transactionViewModel.getHash()); + transactionPropagator.addToPropagationQueue(transactionViewModel.getHash()); } } - @Override - public void addToPropagationQueue(Hash hash) throws Exception{ - if(!solidTransactions.contains(hash)) { - if (solidTransactions.size() >= MAX_SIZE) { - solidTransactions.poll(); - } - solidTransactions.put(hash); - } - } @Override public boolean quickSetSolid(final TransactionViewModel transactionViewModel) throws Exception { @@ -318,7 +307,7 @@ public boolean quickSetSolid(final TransactionViewModel transactionViewModel) th if(solid) { transactionViewModel.updateSolid(true); transactionViewModel.updateHeights(tangle, snapshotProvider.getInitialSnapshot()); - addToPropagationQueue(transactionViewModel.getHash()); + transactionPropagator.addToPropagationQueue(transactionViewModel.getHash()); addToBroadcastQueue(transactionViewModel); return true; } @@ -345,37 +334,61 @@ private boolean checkApproovee(TransactionViewModel approovee) throws Exception return approovee.isSolid(); } - @VisibleForTesting - void propagateSolidTransactions() { - while(!Thread.currentThread().isInterrupted() && solidTransactions.peek() != null) { - try { - Hash hash = solidTransactions.poll(); - TransactionViewModel transaction = fromHash(tangle, hash); - Set approvers = transaction.getApprovers(tangle).getHashes(); - for(Hash h: approvers) { - TransactionViewModel tx = fromHash(tangle, h); - if (quietQuickSetSolid(tx)) { - tx.update(tangle, snapshotProvider.getInitialSnapshot(), "solid|height"); - tipsViewModel.setSolid(h); + + + public class TransactionPropagator { + /** + * A queue for processing transactions with the {@link #propagateSolidTransactions()} call. This will check + * approving transactions with {@link #quickSetSolid(TransactionViewModel)}. + */ + private BlockingQueue solidTransactions = new ArrayBlockingQueue<>(MAX_SIZE); + + /** + * Add to the propagation queue where it will be processed to help solidify approving transactions faster + * @param hash The transaction hash to be removed + * @throws Exception + */ + public void addToPropagationQueue(Hash hash) throws Exception{ + if(!solidTransactions.contains(hash)) { + if (solidTransactions.size() >= MAX_SIZE) { + solidTransactions.poll(); + } + solidTransactions.put(hash); + } + } + + @VisibleForTesting + void propagateSolidTransactions() { + while(!Thread.currentThread().isInterrupted() && solidTransactions.peek() != null) { + try { + Hash hash = solidTransactions.poll(); + TransactionViewModel transaction = fromHash(tangle, hash); + Set approvers = transaction.getApprovers(tangle).getHashes(); + for(Hash h: approvers) { + TransactionViewModel tx = fromHash(tangle, h); + if (quietQuickSetSolid(tx)) { + tx.update(tangle, snapshotProvider.getInitialSnapshot(), "solid|height"); + tipsViewModel.setSolid(h); + } } + } catch (Exception e) { + log.error("Error while propagating solidity upwards", e); } - } catch (Exception e) { - log.error("Error while propagating solidity upwards", e); } } - } - /** - * Perform a {@link #quickSetSolid} while capturing and logging errors - * @param transactionViewModel transaction we try to solidify. - * @return true if we managed to solidify, else false. - */ - private boolean quietQuickSetSolid(TransactionViewModel transactionViewModel) { - try { - return quickSetSolid(transactionViewModel); - } catch (Exception e) { - log.error(e.getMessage(), e); - return false; + /** + * Perform a {@link #quickSetSolid} while capturing and logging errors + * @param transactionViewModel transaction we try to solidify. + * @return true if we managed to solidify, else false. + */ + private boolean quietQuickSetSolid(TransactionViewModel transactionViewModel) { + try { + return quickSetSolid(transactionViewModel); + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } } } } \ No newline at end of file From 3c9414c1d38c1fe69fbf3618eb4256843de0603f Mon Sep 17 00:00:00 2001 From: DyrellC Date: Thu, 26 Mar 2020 18:42:28 -0600 Subject: [PATCH 11/18] Remove unused imports --- .../iri/network/pipeline/TransactionProcessingPipeline.java | 1 - .../network/pipeline/TransactionProcessingPipelineImpl.java | 4 ---- .../java/com/iota/iri/network/pipeline/SolidifyStageTest.java | 1 - 3 files changed, 6 deletions(-) diff --git a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipeline.java b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipeline.java index 9c471a91c0..bf45d44a98 100644 --- a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipeline.java +++ b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipeline.java @@ -1,7 +1,6 @@ package com.iota.iri.network.pipeline; import com.iota.iri.network.neighbor.Neighbor; -import com.iota.iri.service.validation.TransactionSolidifier; import java.nio.ByteBuffer; import java.util.concurrent.BlockingQueue; diff --git a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java index d1e1e88b6d..c4bd0934c7 100644 --- a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java +++ b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java @@ -4,7 +4,6 @@ import com.iota.iri.service.validation.TransactionValidator; import com.iota.iri.conf.NodeConfig; import com.iota.iri.controllers.TipsViewModel; -import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.crypto.batched.BatchedHasher; import com.iota.iri.crypto.batched.BatchedHasherFactory; import com.iota.iri.crypto.batched.HashRequest; @@ -21,10 +20,7 @@ import com.iota.iri.utils.Converter; import java.nio.ByteBuffer; -import java.util.Iterator; -import java.util.LinkedHashSet; import java.util.List; -import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; diff --git a/src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java b/src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java index be3582bf54..1c309adfe7 100644 --- a/src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java +++ b/src/test/java/com/iota/iri/network/pipeline/SolidifyStageTest.java @@ -3,7 +3,6 @@ import com.iota.iri.controllers.TipsViewModel; import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.model.Hash; -import com.iota.iri.model.persistables.Transaction; import com.iota.iri.service.validation.TransactionSolidifier; import com.iota.iri.storage.Tangle; import org.junit.Rule; From d8f32978f488a0cb7a4be9e7faabd26b93d8e537 Mon Sep 17 00:00:00 2001 From: DyrellC Date: Thu, 26 Mar 2020 18:54:24 -0600 Subject: [PATCH 12/18] Add comment to propagator --- .../service/validation/impl/TransactionSolidifierImpl.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java index fea1aa17ab..634066a024 100644 --- a/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java +++ b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java @@ -334,8 +334,10 @@ private boolean checkApproovee(TransactionViewModel approovee) throws Exception return approovee.isSolid(); } - - + /** + * A transaction solidification service that propagates upwards through transactions approving a solid transaction + * and performs {@link #quickSetSolid} checks to determine their solidity as well + */ public class TransactionPropagator { /** * A queue for processing transactions with the {@link #propagateSolidTransactions()} call. This will check From c55d2fd335ae4d4b19072f447323b49aaebe5586 Mon Sep 17 00:00:00 2001 From: DyrellC Date: Thu, 26 Mar 2020 18:59:32 -0600 Subject: [PATCH 13/18] Remove unused txSolidifier private field --- .../iri/network/pipeline/TransactionProcessingPipelineImpl.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java index c4bd0934c7..b4df8333c5 100644 --- a/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java +++ b/src/main/java/com/iota/iri/network/pipeline/TransactionProcessingPipelineImpl.java @@ -71,7 +71,6 @@ public class TransactionProcessingPipelineImpl implements TransactionProcessingP private BatchedHasher batchedHasher; private HashingStage hashingStage; private SolidifyStage solidifyStage; - private TransactionSolidifier txSolidifier; private BlockingQueue preProcessStageQueue = new ArrayBlockingQueue<>(100); private BlockingQueue validationStageQueue = new ArrayBlockingQueue<>(100); @@ -106,7 +105,6 @@ public TransactionProcessingPipelineImpl(NeighborRouter neighborRouter, NodeConf this.batchedHasher = BatchedHasherFactory.create(BatchedHasherFactory.Type.BCTCURL81, 20); this.hashingStage = new HashingStage(batchedHasher); this.solidifyStage = new SolidifyStage(txSolidifier, tipsViewModel, tangle); - this.txSolidifier = txSolidifier; } @Override From ff0ef3599345c5762c0913f400df4514c1b7c817 Mon Sep 17 00:00:00 2001 From: DyrellC Date: Mon, 30 Mar 2020 15:11:15 -0600 Subject: [PATCH 14/18] Remove separate thread logic, check solidity from milestone solidifier --- .../impl/MilestoneSolidifierImpl.java | 2 +- .../validation/TransactionSolidifier.java | 16 ----- .../impl/TransactionSolidifierImpl.java | 61 +------------------ .../impl/TransactionSolidifierImplTest.java | 15 +---- 4 files changed, 4 insertions(+), 90 deletions(-) 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 61b3fa1de2..9db1887a42 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 @@ -341,7 +341,7 @@ private boolean isSolid(Map.Entry currentEntry) { } try { - return transactionSolidifier.addMilestoneToSolidificationQueue(currentEntry.getKey(), SOLIDIFICATION_TRANSACTIONS_LIMIT); + return transactionSolidifier.checkSolidity(currentEntry.getKey(), SOLIDIFICATION_TRANSACTIONS_LIMIT); } catch (Exception e) { log.error("Error while solidifying milestone #" + currentEntry.getValue(), e); diff --git a/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java b/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java index b7b9f16d20..37c57fef42 100644 --- a/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java +++ b/src/main/java/com/iota/iri/service/validation/TransactionSolidifier.java @@ -23,22 +23,6 @@ public interface TransactionSolidifier { */ void shutdown(); - /** - * Add a hash to the solidification queue, and runs an initial {@link #checkSolidity} call. - * - * @param hash Hash of the transaction to solidify - */ - void addToSolidificationQueue(Hash hash); - - /** - * Checks if milestone transaction is solid. Returns true if it is, and if it is not, it adds the hash to the - * solidification queue and returns false. - * - * @param hash Hash of the transaction to solidify - * @param maxToProcess Maximum number of transactions to analyze - * @return True if solid, false if not - */ - boolean addMilestoneToSolidificationQueue(Hash hash, int maxToProcess); /** * Fetch the next transaction in the transactionsToBroadcast set. * @return A {@link TransactionViewModel} object to be broadcast. diff --git a/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java index 634066a024..595c3f48e4 100644 --- a/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java +++ b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java @@ -45,13 +45,6 @@ public class TransactionSolidifierImpl implements TransactionSolidifier { private SilentScheduledExecutorService executorService = new DedicatedScheduledExecutorService( "Transaction Solidifier", log.delegate()); - /** - * A queue for processing transactions with the {@link #checkSolidity(Hash)} call. Once a transaction has been - * marked solid it will be placed into the {@link #transactionsToBroadcast} queue. - */ - private BlockingQueue transactionsToSolidify = new ArrayBlockingQueue<>(MAX_SIZE); - - /** * A set of transactions that will be called by the {@link TransactionProcessingPipeline} to be broadcast to * neighboring nodes. @@ -94,43 +87,6 @@ public void shutdown() { executorService.shutdownNow(); } - /** - *{@inheritDoc} - */ - @Override - public void addToSolidificationQueue(Hash hash){ - try{ - if(!transactionsToSolidify.contains(hash)) { - if (transactionsToSolidify.size() >= MAX_SIZE - 1) { - transactionsToSolidify.remove(); - } - - transactionsToSolidify.put(hash); - } - } catch(Exception e){ - log.error("Error placing transaction into solidification queue",e); - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean addMilestoneToSolidificationQueue(Hash hash, int maxToProcess){ - try{ - TransactionViewModel tx = fromHash(tangle, hash); - if(tx.isSolid()){ - transactionPropagator.addToPropagationQueue(hash); - return true; - } - addToSolidificationQueue(hash); - return false; - }catch(Exception e){ - log.error("Error adding milestone to solidification queue", e); - return false; - } - } - /** *{@inheritDoc} */ @@ -149,18 +105,9 @@ public void clearFromBroadcastQueue(TransactionViewModel transaction){ /** - * Iterate through the {@link #transactionsToSolidify} queue and call {@link #checkSolidity(Hash)} on each hash. - * Solid transactions are then processed into the {@link #transactionsToBroadcast} queue. + * Process any solid transactions present in the {@link TransactionPropagator}. */ private void processTransactionsToSolidify(){ - Hash hash; - if((hash = transactionsToSolidify.poll()) != null) { - try { - checkSolidity(hash); - } catch (Exception e) { - log.info(e.getMessage()); - } - } transactionPropagator.propagateSolidTransactions(); } @@ -271,12 +218,6 @@ private void addToBroadcastQueue(TransactionViewModel tvm) { } } - @VisibleForTesting - Set getSolidificationQueue(){ - return new LinkedHashSet<>(transactionsToSolidify); - } - - @Override public void updateStatus(TransactionViewModel transactionViewModel) throws Exception { transactionRequester.clearTransactionRequest(transactionViewModel.getHash()); diff --git a/src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java b/src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java index 0220fcaa82..b9353cd72b 100644 --- a/src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java +++ b/src/test/java/com/iota/iri/service/validation/impl/TransactionSolidifierImplTest.java @@ -98,22 +98,11 @@ public void verifyTxIsNotSolid() throws Exception { assertFalse("Expected transaction to fail solidity check", txSolidifier.checkSolidity(tx.getHash())); } - @Test - public void getSolidificationQueue() throws Exception { - TransactionViewModel mainTx = getTxWithBranchAndTrunk(); - for(int i = 0; i < 10; i++) { - TransactionViewModel tx = getTxWithBranchAndTrunk(); - txSolidifier.addToSolidificationQueue(tx.getHash()); - } - txSolidifier.addToSolidificationQueue(mainTx.getHash()); - assertTrue("Expected transaction to be present in the solidification queue", - txSolidifier.getSolidificationQueue().contains(mainTx.getHash())); - } @Test public void verifyTransactionIsProcessedFully() throws Exception { TransactionViewModel tx = getTxWithBranchAndTrunk(); - txSolidifier.addToSolidificationQueue(tx.getHash()); + txSolidifier.checkSolidity(tx.getHash()); //Time to process through the steps Thread.sleep(1000); @@ -132,7 +121,7 @@ public void verifyTransactionIsProcessedFully() throws Exception { @Test public void verifyInconsistentTransactionIsNotProcessedFully() throws Exception { TransactionViewModel tx = getTxWithoutBranchAndTrunk(); - txSolidifier.addToSolidificationQueue(tx.getHash()); + txSolidifier.checkSolidity(tx.getHash()); //Time to process through the steps Thread.sleep(1000); From fd7e9334369963e0ceb4e873ba01b23dd174a31f Mon Sep 17 00:00:00 2001 From: DyrellC Date: Mon, 30 Mar 2020 15:34:58 -0600 Subject: [PATCH 15/18] LinkedHashSet -> ArrayDeque in checkSolidity --- .../iri/service/validation/impl/TransactionSolidifierImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java index 595c3f48e4..3238de51da 100644 --- a/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java +++ b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java @@ -132,7 +132,7 @@ public boolean checkSolidity(Hash hash, int maxProcessedTransactions) throws Exc maxProcessedTransactions += analyzedHashes.size(); } boolean solid = true; - final Queue nonAnalyzedTransactions = new LinkedList<>(Collections.singleton(hash)); + final Deque nonAnalyzedTransactions = new ArrayDeque<>(Collections.singleton(hash)); Hash hashPointer; while ((hashPointer = nonAnalyzedTransactions.poll()) != null) { if (!analyzedHashes.add(hashPointer)) { From 666dadf3f91cbf28866a79a24f1e706e1a3b0127 Mon Sep 17 00:00:00 2001 From: DyrellC Date: Mon, 30 Mar 2020 15:40:43 -0600 Subject: [PATCH 16/18] Update maxProcessedTransactions value determination --- .../service/validation/impl/TransactionSolidifierImpl.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java index 3238de51da..2a75ec8507 100644 --- a/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java +++ b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java @@ -128,9 +128,7 @@ public boolean checkSolidity(Hash hash, int maxProcessedTransactions) throws Exc return true; } LinkedHashSet analyzedHashes = new LinkedHashSet<>(snapshotProvider.getInitialSnapshot().getSolidEntryPoints().keySet()); - if(maxProcessedTransactions != Integer.MAX_VALUE) { - maxProcessedTransactions += analyzedHashes.size(); - } + maxProcessedTransactions = Math.addExact(maxProcessedTransactions, analyzedHashes.size()); boolean solid = true; final Deque nonAnalyzedTransactions = new ArrayDeque<>(Collections.singleton(hash)); Hash hashPointer; From f31de917de3029268278644e0cf2038ea56c823e Mon Sep 17 00:00:00 2001 From: DyrellC Date: Mon, 30 Mar 2020 15:49:01 -0600 Subject: [PATCH 17/18] Make Transaction Propagator private --- .../iri/service/validation/impl/TransactionSolidifierImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java index 2a75ec8507..b147168e82 100644 --- a/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java +++ b/src/main/java/com/iota/iri/service/validation/impl/TransactionSolidifierImpl.java @@ -277,7 +277,7 @@ private boolean checkApproovee(TransactionViewModel approovee) throws Exception * A transaction solidification service that propagates upwards through transactions approving a solid transaction * and performs {@link #quickSetSolid} checks to determine their solidity as well */ - public class TransactionPropagator { + private class TransactionPropagator { /** * A queue for processing transactions with the {@link #propagateSolidTransactions()} call. This will check * approving transactions with {@link #quickSetSolid(TransactionViewModel)}. From 5f70a3d58fed2f7467ca2d8309258dcb3f633819 Mon Sep 17 00:00:00 2001 From: DyrellC Date: Thu, 2 Apr 2020 08:56:21 -0600 Subject: [PATCH 18/18] Typo correction --- .../java/com/iota/iri/network/pipeline/SolidifyStage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java b/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java index 4db01d6a7c..a7b792dc5e 100644 --- a/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java +++ b/src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java @@ -28,7 +28,7 @@ public class SolidifyStage implements Stage { /** * Constructor for the {@link SolidifyStage}. * - * @param txSolidifier Transaction validator implementation for determining the validity of a transaction + * @param txSolidifier Transaction solidifier implementation for determining the validity of a transaction * @param tipsViewModel Used for broadcasting random solid tips if the subject transaction is unsolid * @param tangle A reference to the nodes DB */ @@ -41,7 +41,7 @@ public SolidifyStage(TransactionSolidifier txSolidifier, TipsViewModel tipsViewM /** * Processes the payload of the {@link ProcessingContext} as a {@link SolidifyPayload}. First the transaction will * be checked for solidity and validity. If the transaction is already solid or can be set solid quickly by the - * transaction validator, the transaction is passed to the {@link BroadcastStage}. If not, a random solid tip is + * transaction solidifier, the transaction is passed to the {@link BroadcastStage}. If not, a random solid tip is * pulled form the {@link TipsViewModel} to be broadcast instead. * * @param ctx The context to process