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

Commit

Permalink
adds QuickTransactionSolidifier
Browse files Browse the repository at this point in the history
  • Loading branch information
luca-moser committed Sep 23, 2019
1 parent 3960ba9 commit 0d72605
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 8 deletions.
9 changes: 9 additions & 0 deletions src/main/java/com/iota/iri/Iota.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.iota.iri.service.snapshot.impl.LocalSnapshotManagerImpl;
import com.iota.iri.service.snapshot.impl.SnapshotProviderImpl;
import com.iota.iri.service.snapshot.impl.SnapshotServiceImpl;
import com.iota.iri.service.solidifier.impl.QuickTransactionSolidifier;
import com.iota.iri.service.spentaddresses.SpentAddressesException;
import com.iota.iri.service.spentaddresses.impl.SpentAddressesProviderImpl;
import com.iota.iri.service.spentaddresses.impl.SpentAddressesServiceImpl;
Expand Down Expand Up @@ -115,6 +116,8 @@ public class Iota {

public final Tangle tangle;
public final TransactionValidator transactionValidator;

public final QuickTransactionSolidifier quickTransactionSolidifier;
public final TransactionRequester transactionRequester;
public final TipsRequesterImpl tipRequester;
public final TransactionProcessingPipeline txPipeline;
Expand Down Expand Up @@ -152,6 +155,7 @@ public Iota(IotaConfig configuration)
neighborRouter = new NeighborRouter();
txPipeline = new TransactionProcessingPipelineImpl();
tipRequester = new TipsRequesterImpl();
quickTransactionSolidifier = new QuickTransactionSolidifier();

// legacy code
bundleValidator = new BundleValidator();
Expand Down Expand Up @@ -200,6 +204,8 @@ public void init() throws Exception {
seenMilestonesRetriever.start();
milestoneSolidifier.start();

quickTransactionSolidifier.start();

if (localSnapshotManager != null) {
localSnapshotManager.start(latestMilestoneTracker);
}
Expand Down Expand Up @@ -232,6 +238,8 @@ private void injectDependencies() throws SnapshotException, TransactionPruningEx
if (transactionPruner != null) {
transactionPruner.init(tangle, snapshotProvider, spentAddressesService, spentAddressesProvider, tipsViewModel, configuration);
}

quickTransactionSolidifier.init(configuration, tangle, snapshotProvider, latestMilestoneTracker, transactionValidator);
neighborRouter.init(configuration, configuration, transactionRequester, txPipeline);
txPipeline.init(neighborRouter, configuration, transactionValidator, tangle, snapshotProvider, tipsViewModel,
latestMilestoneTracker, transactionRequester);
Expand Down Expand Up @@ -299,6 +307,7 @@ public void shutdown() throws Exception {
neighborRouter.shutdown();
transactionValidator.shutdown();
tangle.shutdown();
quickTransactionSolidifier.shutdown();

// free the resources of the snapshot provider last because all other instances need it
snapshotProvider.shutdown();
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/iota/iri/TransactionValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ public void updateStatus(TransactionViewModel transactionViewModel) throws Excep
* @param transactionViewModel transaction we try to solidify.
* @return <tt>true</tt> if we managed to solidify, else <tt>false</tt>.
*/
private boolean quietQuickSetSolid(TransactionViewModel transactionViewModel) {
public boolean quietQuickSetSolid(TransactionViewModel transactionViewModel) {
try {
return quickSetSolid(transactionViewModel);
} catch (Exception e) {
Expand Down
61 changes: 55 additions & 6 deletions src/main/java/com/iota/iri/conf/BaseIotaConfig.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package com.iota.iri.conf;

import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.iota.iri.crypto.SpongeFactory;
import com.iota.iri.model.Hash;
import com.iota.iri.model.HashFactory;
Expand Down Expand Up @@ -30,7 +35,7 @@ public abstract class BaseIotaConfig implements IotaConfig {

private boolean help;
private boolean testnet = false;

//API
protected int port = Defaults.PORT;
protected String apiHost = Defaults.API_HOST;
Expand All @@ -41,7 +46,7 @@ public abstract class BaseIotaConfig implements IotaConfig {
protected int maxGetTrytes = Defaults.MAX_GET_TRYTES;
protected int maxBodyLength = Defaults.MAX_BODY_LENGTH;
protected String remoteAuth = Defaults.REMOTE_AUTH;

//We don't have a REMOTE config but we have a remote flag. We must add a field for JCommander
private boolean remote;

Expand Down Expand Up @@ -92,6 +97,11 @@ public abstract class BaseIotaConfig implements IotaConfig {
protected int tipSelectionTimeoutSec = Defaults.TIP_SELECTION_TIMEOUT_SEC;
private int maxAnalyzedTransactions = Defaults.BELOW_MAX_DEPTH_TRANSACTION_LIMIT;

//Tip Solidification
protected boolean tipSolidifierEnabled = Defaults.TIP_SOLIDIFIER_ENABLED;
protected int solidifierDepth = Defaults.SOLIDIFIER_DEPTH;
protected int solidifierIntervalMillisec = Defaults.SOLIDIFIER_INTERVAL_MILLISEC;

//PearlDiver
protected int powThreads = Defaults.POW_THREADS;

Expand Down Expand Up @@ -131,12 +141,12 @@ public JCommander parseConfigFromArgs(String[] args) throws ParameterException {
public boolean isHelp() {
return help;
}

@Override
public boolean isTestnet() {
return testnet;
}

@JsonIgnore
@Parameter(names = {Config.TESTNET_FLAG}, description = Config.Descriptions.TESTNET, arity = 1)
protected void setTestnet(boolean testnet) {
Expand Down Expand Up @@ -165,7 +175,7 @@ public String getApiHost() {
if (remote) {
return "0.0.0.0";
}

return apiHost;
}

Expand Down Expand Up @@ -795,13 +805,47 @@ protected void setTipSelectionTimeoutSec(int tipSelectionTimeoutSec) {
this.tipSelectionTimeoutSec = tipSelectionTimeoutSec;
}

@Override
public boolean isTipSolidifierEnabled() {
return tipSolidifierEnabled;
}

@JsonProperty
@Parameter(names = "--tip-solidifier", description = SolidificationConfig.Descriptions.TIP_SOLIDIFIER,
arity = 1)
protected void setTipSolidifierEnabled(boolean tipSolidifierEnabled) {
this.tipSolidifierEnabled = tipSolidifierEnabled;
}

@Override
public int getSolidifierDepth() {
return solidifierDepth;
}

@JsonProperty
@Parameter(names = {"--solidifier-depth"}, description = SolidificationConfig.Descriptions.SOLIDIFIER_DEPTH)
protected void setSolidifierDepth(int solidifierDepth) {
this.solidifierDepth = solidifierDepth;
}

@Override
public int getSolidifierIntervalMillisec() {
return solidifierIntervalMillisec;
}

@JsonProperty
@Parameter(names = {"--solidifier-interval-millisec"}, description = SolidificationConfig.Descriptions.SOLIDIFIER_INTERVAL_MILLISEC)
protected void setSolidifierIntervalMillisec(int solidifierIntervalMillisec) {
this.solidifierIntervalMillisec = solidifierIntervalMillisec;
}

@Override
public int getBelowMaxDepthTransactionLimit() {
return maxAnalyzedTransactions;
}

@JsonProperty
@Parameter(names = "--max-analyzed-transactions",
@Parameter(names = "--max-analyzed-transactions",
description = TipSelConfig.Descriptions.BELOW_MAX_DEPTH_TRANSACTION_LIMIT)
protected void setBelowMaxDepthTransactionLimit(int maxAnalyzedTransactions) {
this.maxAnalyzedTransactions = maxAnalyzedTransactions;
Expand Down Expand Up @@ -876,6 +920,11 @@ public interface Defaults {
double ALPHA = 0.001d;
int TIP_SELECTION_TIMEOUT_SEC = 60;

//Tip solidification
boolean TIP_SOLIDIFIER_ENABLED = true;
int SOLIDIFIER_DEPTH = 3;
int SOLIDIFIER_INTERVAL_MILLISEC = 10000;

//PearlDiver
int POW_THREADS = 0;

Expand Down
28 changes: 27 additions & 1 deletion src/main/java/com/iota/iri/conf/SolidificationConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,34 @@
*/
public interface SolidificationConfig extends Config {

/**
* Default Value: {@value BaseIotaConfig.Defaults#TIP_SOLIDIFIER_ENABLED}
*
* @return {@value SolidificationConfig.Descriptions#TIP_SOLIDIFIER}
*/
boolean isTipSolidifierEnabled();

/**
* The depth the solidifier will use. {@value BaseIotaConfig.Defaults#SOLIDIFIER_DEPTH}
*
* @return {@value BaseIotaConfig.Defaults#SOLIDIFIER_DEPTH}
*/
int getSolidifierDepth();

/**
* Returns the interval at which the solidifer will run. {@value BaseIotaConfig.Defaults#SOLIDIFIER_INTERVAL_MILLISEC}
*
* @return {@value BaseIotaConfig.Defaults#SOLIDIFIER_INTERVAL_MILLISEC}
*/
int getSolidifierIntervalMillisec();

/**
* Field descriptions
*/
interface Descriptions { }
interface Descriptions {

String TIP_SOLIDIFIER = "Scan the current tips and attempt to mark them as solid";
String SOLIDIFIER_DEPTH = "The depth at which the solidifer will start";
String SOLIDIFIER_INTERVAL_MILLISEC = "The interval at which the solidifier will run";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.iota.iri.service.solidifier;

/**
* The {@link TransactionSolidifier} continuously tries to solidify transactions.
*/
public interface TransactionSolidifier {

/**
* Starts the background worker that continuously tries to solidify transactions.
*/
void start();

/**
* Solidifies transactions. Which transactions and how are getting solidified is an implementation detail.
*/
void solidify();

/**
* Stops the background worker that continuously solidifies transactions.
*/
void shutdown();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.iota.iri.service.solidifier.impl;

import com.iota.iri.TransactionValidator;
import com.iota.iri.conf.SolidificationConfig;
import com.iota.iri.controllers.MilestoneViewModel;
import com.iota.iri.controllers.TransactionViewModel;
import com.iota.iri.service.milestone.LatestMilestoneTracker;
import com.iota.iri.service.snapshot.SnapshotProvider;
import com.iota.iri.service.solidifier.TransactionSolidifier;
import com.iota.iri.storage.Tangle;
import com.iota.iri.utils.dag.DAGHelper;
import com.iota.iri.utils.thread.DedicatedScheduledExecutorService;
import com.iota.iri.utils.thread.SilentScheduledExecutorService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
* The {@link QuickTransactionSolidifier} starts at the defined depth parameter to walk up the graph towards the tips
* and executes the {@link TransactionValidator#quietQuickSetSolid(TransactionViewModel)} on each traversed transaction.
*/
public class QuickTransactionSolidifier implements TransactionSolidifier {

private static final Logger log = LoggerFactory.getLogger(QuickTransactionSolidifier.class);

private final SilentScheduledExecutorService executorService = new DedicatedScheduledExecutorService(
"Quick Transaction Solidifier", log);

// external
private SolidificationConfig solidificationConfig;
private Tangle tangle;
private SnapshotProvider snapshotProvider;
private LatestMilestoneTracker latestMilestoneTracker;
private TransactionValidator transactionValidator;

/**
* <p>
* This method initializes the instance and registers its dependencies.
* </p>
* <p>
* It simply stores the passed in values in their corresponding private properties.
* </p>
* <p>
* Note: Instead of handing over the dependencies in the constructor, we register them lazy. This allows us to have
* circular dependencies because the instantiation is separated from the dependency injection. To reduce the
* amount of code that is necessary to correctly instantiate this class, we return the instance itself which
* allows us to still instantiate, initialize and assign in one line - see Example:
* </p>
* {@code quickTransactionSolidifier = new QuickTransactionSolidifier().init(...);}
*
* @param solidificationConfig the configuration holding parameters for solidification
* @param tangle Tangle object which acts as a database interface
* @param snapshotProvider data provider for the snapshots that are relevant for the node
* @param latestMilestoneTracker the tracker holding the latest known milestone index
* @param transactionValidator the TransactionValidator which holds the logic for checking quick solidification
* @return the initialized instance itself to allow chaining
*/
public QuickTransactionSolidifier init(SolidificationConfig solidificationConfig, Tangle tangle, SnapshotProvider snapshotProvider,
LatestMilestoneTracker latestMilestoneTracker, TransactionValidator transactionValidator) {
this.solidificationConfig = solidificationConfig;
this.tangle = tangle;
this.snapshotProvider = snapshotProvider;
this.latestMilestoneTracker = latestMilestoneTracker;
this.transactionValidator = transactionValidator;
return this;
}

@Override
public void start() {
executorService.silentScheduleWithFixedDelay(this::solidify, 0,
solidificationConfig.getSolidifierIntervalMillisec(), TimeUnit.MILLISECONDS);
}

@Override
public void solidify() {
int latestMilestoneIndex = latestMilestoneTracker.getLatestMilestoneIndex();
int depth = solidificationConfig.getSolidifierDepth();

// don't solidify if not enough milestones were issued yet
if (latestMilestoneIndex - depth <= 0) {
return;
}

// we only try to quick solidify transactions when we are above the defined solidification depth
if (snapshotProvider.getLatestSnapshot().getIndex() < latestMilestoneIndex - depth) {
return;
}

DAGHelper dagHelper = DAGHelper.get(tangle);
long start = System.currentTimeMillis();
try {
MilestoneViewModel milestone = MilestoneViewModel.get(tangle, latestMilestoneIndex - depth);
// if we don't have the milestone which we wanted to use as a starting point, we simply don't run.
if (milestone == null) {
return;
}
AtomicInteger updated = new AtomicInteger();
AtomicInteger traversed = new AtomicInteger();
dagHelper.traverseApprovers(milestone.getHash(), tvm -> !Thread.currentThread().isInterrupted(), tvm -> {
try {
if (transactionValidator.quietQuickSetSolid(tvm)) {
tvm.update(tangle, snapshotProvider.getInitialSnapshot(), "solid|height");
updated.incrementAndGet();
}
traversed.incrementAndGet();
} catch (Exception e) {
log.error("error while trying to quick set solid transaction {}. reason: {}", tvm.getHash(),
e.getMessage());
}
});

log.info("updated {} and traversed {} transactions, took {} ms", updated.get(), traversed.get(),
System.currentTimeMillis() - start);
} catch (Exception e) {
log.error("error occurred during quick solidification run: {}", e.getMessage());
}
}

@Override
public void shutdown() {
executorService.shutdownNow();
}
}

0 comments on commit 0d72605

Please sign in to comment.