Skip to content
This repository has been archived by the owner on Aug 23, 2020. It is now read-only.

Commit

Permalink
Add transaction solidifier (#1805)
Browse files Browse the repository at this point in the history
* Move validation to validation service package

* Add Transaction Solidifier

* Update Transaction Validator

* Update Unit Tests

* Remove tip field from solidify stage

* Move broadcast queue retreival to solidify stage

* Undo auto-formatting

* More autoformatting

* Re-remove refillBroadcast

* Move propagation logic to inner class

* Remove unused imports

* Add comment to propagator

* Remove unused txSolidifier private field

* Remove separate thread logic, check solidity from milestone solidifier

* LinkedHashSet -> ArrayDeque in checkSolidity

* Update maxProcessedTransactions value determination

* Make Transaction Propagator private

* Typo correction

Co-authored-by: Gal Rogozinski <[email protected]>
  • Loading branch information
DyrellC and Gal Rogozinski authored Apr 2, 2020
1 parent 18421e3 commit 739ea7a
Show file tree
Hide file tree
Showing 22 changed files with 1,037 additions and 548 deletions.
12 changes: 7 additions & 5 deletions src/main/java/com/iota/iri/Iota.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -96,6 +97,8 @@ public class Iota {

public final MilestoneSolidifier milestoneSolidifier;

public final TransactionSolidifier transactionSolidifier;

public final BundleValidator bundleValidator;

public final Tangle tangle;
Expand Down Expand Up @@ -127,7 +130,7 @@ public Iota(IotaConfig configuration, SpentAddressesProvider spentAddressesProvi
TransactionRequester transactionRequester, NeighborRouter neighborRouter,
TransactionProcessingPipeline transactionProcessingPipeline, TipsRequester tipsRequester,
TipsViewModel tipsViewModel, TipSelector tipsSelector, LocalSnapshotsPersistenceProvider localSnapshotsDb,
CacheManager cacheManager) {
CacheManager cacheManager, TransactionSolidifier transactionSolidifier) {
this.configuration = configuration;

this.ledgerService = ledgerService;
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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));
Expand Down Expand Up @@ -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();
Expand All @@ -270,7 +273,6 @@ public void shutdown() throws Exception {
tipsRequester.shutdown();
txPipeline.shutdown();
neighborRouter.shutdown();
transactionValidator.shutdown();
localSnapshotsDb.shutdown();
tangle.shutdown();

Expand Down
24 changes: 16 additions & 8 deletions src/main/java/com/iota/iri/MainInjectionConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -172,12 +180,12 @@ Iota provideIota(SpentAddressesProvider spentAddressesProvider, SpentAddressesSe
TransactionRequester transactionRequester, NeighborRouter neighborRouter,
TransactionProcessingPipeline transactionProcessingPipeline, TipsRequester tipsRequester,
TipsViewModel tipsViewModel, TipSelector tipsSelector, LocalSnapshotsPersistenceProvider localSnapshotsDb,
CacheManager cacheManager) {
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
Expand All @@ -192,8 +200,8 @@ 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);
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
Expand Down
22 changes: 0 additions & 22 deletions src/main/java/com/iota/iri/controllers/TransactionViewModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -778,28 +778,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<Hash> 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -46,9 +47,9 @@ TipsRequester provideTipsRequester(NeighborRouter neighborRouter, Tangle tangle,
TransactionProcessingPipeline provideTransactionProcessingPipeline(NeighborRouter neighborRouter,
TransactionValidator txValidator, Tangle tangle, SnapshotProvider snapshotProvider,
TipsViewModel tipsViewModel, LatestMilestoneTracker latestMilestoneTracker,
TransactionRequester transactionRequester) {
TransactionRequester transactionRequester, TransactionSolidifier transactionSolidifier) {
return new TransactionProcessingPipelineImpl(neighborRouter, configuration, txValidator, tangle,
snapshotProvider, tipsViewModel, latestMilestoneTracker, transactionRequester);
snapshotProvider, tipsViewModel, latestMilestoneTracker, transactionRequester, transactionSolidifier);
}

@Singleton
Expand Down
24 changes: 12 additions & 12 deletions src/main/java/com/iota/iri/network/pipeline/ReceivedStage.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -19,28 +19,28 @@ 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;
}

/**
* 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}
*/
Expand All @@ -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())){
Expand All @@ -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;
}
}
}
39 changes: 39 additions & 0 deletions src/main/java/com/iota/iri/network/pipeline/SolidifyPayload.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
94 changes: 94 additions & 0 deletions src/main/java/com/iota/iri/network/pipeline/SolidifyStage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
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.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;

/**
* Constructor for the {@link SolidifyStage}.
*
* @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
*/
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 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
* @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)) {
// 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;
}

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{
// First check if there is a transaction available to broadcast from the broadcast queue
TransactionViewModel tip = txSolidifier.getNextTxInBroadcastQueue();

// 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;
}

tip = fromHash(tangle, tipHash);
}

ctx.setNextStage(TransactionProcessingPipeline.Stage.BROADCAST);
ctx.setPayload(new BroadcastPayload(payload.getOriginNeighbor(), tip));

return ctx;
}
}
Loading

0 comments on commit 739ea7a

Please sign in to comment.