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 Jun 5, 2019
1 parent 26f734c commit 6400f4b
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 9 deletions.
5 changes: 5 additions & 0 deletions src/main/java/com/iota/iri/Iota.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,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 @@ -101,6 +102,7 @@ public class Iota {
public final Tangle tangle;
public final TransactionValidator transactionValidator;
public final TipsSolidifier tipsSolidifier;
public final QuickTransactionSolidifier quickTransactionSolidifier;
public final TransactionRequester transactionRequester;
public final Node node;
public final UDPReceiver udpReceiver;
Expand Down Expand Up @@ -137,6 +139,7 @@ public Iota(IotaConfig configuration) throws TransactionPruningException, Snapsh
? new AsyncTransactionPruner()
: null;
transactionRequesterWorker = new TransactionRequesterWorkerImpl();
quickTransactionSolidifier = new QuickTransactionSolidifier();

// legacy code
bundleValidator = new BundleValidator();
Expand Down Expand Up @@ -190,6 +193,7 @@ public void init() throws Exception {
seenMilestonesRetriever.start();
milestoneSolidifier.start();
transactionRequesterWorker.start();
quickTransactionSolidifier.start();

if (localSnapshotManager != null) {
localSnapshotManager.start(latestMilestoneTracker);
Expand Down Expand Up @@ -222,6 +226,7 @@ private void injectDependencies() throws SnapshotException, TransactionPruningEx
transactionPruner.init(tangle, snapshotProvider, spentAddressesService, spentAddressesProvider, tipsViewModel, configuration);
}
transactionRequesterWorker.init(tangle, transactionRequester, tipsViewModel, node);
quickTransactionSolidifier.init(configuration, tangle, snapshotProvider, latestMilestoneTracker, transactionValidator);
}

private void rescanDb() throws Exception {
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
39 changes: 32 additions & 7 deletions src/main/java/com/iota/iri/conf/BaseIotaConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.beust.jcommander.ParameterException;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.iota.iri.IRI;
import com.iota.iri.crypto.SpongeFactory;
import com.iota.iri.model.Hash;
import com.iota.iri.model.HashFactory;
Expand All @@ -30,7 +29,7 @@ public abstract class BaseIotaConfig implements IotaConfig {

private boolean help;
private boolean testnet = false;

//API
protected int port = Defaults.API_PORT;
protected String apiHost = Defaults.API_HOST;
Expand All @@ -41,7 +40,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 @@ -98,6 +97,8 @@ public abstract class BaseIotaConfig implements IotaConfig {

//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 @@ -138,12 +139,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 @@ -172,7 +173,7 @@ public String getApiHost() {
if (remote) {
return "0.0.0.0";
}

return apiHost;
}

Expand Down Expand Up @@ -852,13 +853,35 @@ 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 @@ -940,6 +963,8 @@ public interface Defaults {

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

//PearlDiver
int POW_THREADS = 0;
Expand Down
18 changes: 17 additions & 1 deletion src/main/java/com/iota/iri/conf/SolidificationConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,28 @@ public interface SolidificationConfig extends Config {
* @return {@value SolidificationConfig.Descriptions#TIP_SOLIDIFIER}
*/
boolean isTipSolidifierEnabled();


/**
* The depth the solidifier will use.
*
* @return the depth the solidifier will use.
*/
int getSolidifierDepth();

/**
* Returns the interval at which the solidifer will run.
*
* @return the interval at which the solidifer will run
*/
int getSolidifierIntervalMillisec();

/**
* Field 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,121 @@
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() {
// we only try to quick solidify transactions when we are not synchronized and below the depth of the
// quick solidification parameter
int latestMilestoneIndex = latestMilestoneTracker.getLatestMilestoneIndex();
int depth = solidificationConfig.getSolidifierDepth();
if (latestMilestoneIndex - depth <= 0
|| 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 6400f4b

Please sign in to comment.