Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 8 additions & 17 deletions src/main/java/net/helix/pendulum/Pendulum.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,17 @@
import net.helix.pendulum.network.replicator.Replicator;
import net.helix.pendulum.service.TipsSolidifier;
import net.helix.pendulum.service.ledger.impl.LedgerServiceImpl;
import net.helix.pendulum.service.milestone.impl.LatestSolidMilestoneTrackerImpl;
import net.helix.pendulum.service.milestone.impl.MilestoneServiceImpl;
import net.helix.pendulum.service.milestone.impl.MilestoneSolidifierImpl;
import net.helix.pendulum.service.milestone.impl.MilestoneTrackerImpl;
import net.helix.pendulum.service.milestone.impl.SeenMilestonesRetrieverImpl;
import net.helix.pendulum.service.milestone.VirtualTransactionService;
import net.helix.pendulum.service.milestone.impl.*;
import net.helix.pendulum.service.snapshot.SnapshotException;
import net.helix.pendulum.service.snapshot.impl.LocalSnapshotManagerImpl;
import net.helix.pendulum.service.snapshot.impl.SnapshotProviderImpl;
import net.helix.pendulum.service.snapshot.impl.SnapshotServiceImpl;
import net.helix.pendulum.service.spentaddresses.SpentAddressesException;
import net.helix.pendulum.service.spentaddresses.impl.SpentAddressesProviderImpl;
import net.helix.pendulum.service.spentaddresses.impl.SpentAddressesServiceImpl;
import net.helix.pendulum.service.tipselection.EntryPointSelector;
import net.helix.pendulum.service.tipselection.RatingCalculator;
import net.helix.pendulum.service.tipselection.TailFinder;
import net.helix.pendulum.service.tipselection.TipSelector;
import net.helix.pendulum.service.tipselection.Walker;
import net.helix.pendulum.service.tipselection.impl.CumulativeWeightCalculator;
import net.helix.pendulum.service.tipselection.impl.EntryPointSelectorImpl;
import net.helix.pendulum.service.tipselection.impl.TailFinderImpl;
import net.helix.pendulum.service.tipselection.impl.TipSelectorImpl;
import net.helix.pendulum.service.tipselection.impl.WalkerAlpha;
import net.helix.pendulum.service.tipselection.*;
import net.helix.pendulum.service.tipselection.impl.*;
import net.helix.pendulum.service.transactionpruning.TransactionPruningException;
import net.helix.pendulum.service.transactionpruning.async.AsyncTransactionPruner;
import net.helix.pendulum.service.validatormanager.impl.CandidateSolidifierImpl;
Expand Down Expand Up @@ -105,6 +94,7 @@ public class Pendulum {
public final MilestoneSolidifierImpl milestoneSolidifier;
public final CandidateSolidifierImpl candidateSolidifier;
public final TransactionRequesterWorkerImpl transactionRequesterWorker;
public final VirtualTransactionService virtualTransactionService;

public final Tangle tangle;
public final TransactionValidator transactionValidator;
Expand Down Expand Up @@ -149,15 +139,15 @@ public Pendulum(PendulumConfig configuration) throws TransactionPruningException
? new AsyncTransactionPruner()
: null;
transactionRequesterWorker = new TransactionRequesterWorkerImpl();

virtualTransactionService = new VirtualTransactionServiceImpl();
// legacy code
bundleValidator = new BundleValidator();
tangle = new Tangle();
tipsViewModel = new TipsViewModel();
transactionRequester = new TransactionRequester(tangle, snapshotProvider);
transactionValidator = new TransactionValidator(tangle, snapshotProvider, tipsViewModel, transactionRequester, configuration);
node = new Node(tangle, snapshotProvider, transactionValidator, transactionRequester, tipsViewModel,
latestMilestoneTracker, configuration);
latestMilestoneTracker, virtualTransactionService, configuration);
replicator = new Replicator(node, configuration);
udpReceiver = new UDPReceiver(node, configuration);
tipsSolidifier = new TipsSolidifier(tangle, transactionValidator, tipsViewModel, configuration);
Expand Down Expand Up @@ -228,6 +218,7 @@ private void injectDependencies() throws SnapshotException, TransactionPruningEx
localSnapshotManager.init(snapshotProvider, snapshotService, transactionPruner, configuration);
}
milestoneService.init(tangle, snapshotProvider, snapshotService, transactionValidator, configuration);
virtualTransactionService.init(tangle, snapshotProvider, transactionValidator);
validatorManagerService.init(tangle, snapshotProvider, snapshotService, configuration);
candidateTracker.init(tangle, snapshotProvider, validatorManagerService, candidateSolidifier, configuration);
latestMilestoneTracker.init(tangle, snapshotProvider, milestoneService, milestoneSolidifier, candidateTracker, configuration);
Expand Down
16 changes: 6 additions & 10 deletions src/main/java/net/helix/pendulum/SignedFiles.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
package net.helix.pendulum;

import net.helix.pendulum.crypto.Merkle;
import net.helix.pendulum.crypto.Sha3;
import net.helix.pendulum.crypto.Sponge;
import net.helix.pendulum.crypto.SpongeFactory;
import net.helix.pendulum.crypto.Winternitz;
import net.helix.pendulum.crypto.merkle.MerkleTree;
import net.helix.pendulum.crypto.merkle.impl.MerkleTreeImpl;
import net.helix.pendulum.model.Hash;
import org.apache.commons.lang3.ArrayUtils;
import org.bouncycastle.util.encoders.Hex;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.io.*;
import java.util.Arrays;

public class SignedFiles {
Expand All @@ -31,7 +27,7 @@ private static boolean validateSignature(String signatureFilename, String public
byte[] bundle = Winternitz.normalizedBundle(digest);
byte[] root;
int i;

MerkleTree merkle = new MerkleTreeImpl();
try (InputStream inputStream = SignedFiles.class.getResourceAsStream(signatureFilename);
BufferedReader reader = new BufferedReader((inputStream == null)
? new FileReader(signatureFilename) : new InputStreamReader(inputStream))) {
Expand All @@ -46,7 +42,7 @@ private static boolean validateSignature(String signatureFilename, String public

if ((line = reader.readLine()) != null) {
byte[] lineBytes = Hex.decode(line);
root = Merkle.getMerkleRoot(mode, Winternitz.address(mode, digests), lineBytes, 0, index, depth);
root = merkle.getMerkleRoot(mode, Winternitz.address(mode, digests), lineBytes, 0, index, depth);

} else {
root = Winternitz.address(mode, digests);
Expand Down Expand Up @@ -75,7 +71,7 @@ private static byte[] digestFile(String filename, Sponge sha3) throws IOExceptio
messageBytes = Hash.NULL_HASH.bytes();
}
int requiredLength = (int) Math.ceil(messageBytes.length / 32.0) * 32;
byte[] finalizedMessage = Merkle.padding(messageBytes, requiredLength);
byte[] finalizedMessage = MerkleTree.padding(messageBytes, requiredLength);
// crypto snapshot message
sha3.absorb(finalizedMessage, 0, finalizedMessage.length);
byte[] signature = new byte[Sha3.HASH_LENGTH];
Expand Down
93 changes: 42 additions & 51 deletions src/main/java/net/helix/pendulum/TransactionValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,10 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

import static net.helix.pendulum.controllers.TransactionViewModel.PREFILLED_SLOT;
import static net.helix.pendulum.controllers.TransactionViewModel.SIZE;
import static net.helix.pendulum.controllers.TransactionViewModel.VALUE_OFFSET;
import static net.helix.pendulum.controllers.TransactionViewModel.VALUE_SIZE;
import static net.helix.pendulum.controllers.TransactionViewModel.VALUE_USABLE_SIZE;
import static net.helix.pendulum.controllers.TransactionViewModel.fromHash;
import static net.helix.pendulum.controllers.TransactionViewModel.updateSolidTransactions;
import static net.helix.pendulum.controllers.TransactionViewModel.*;

public class TransactionValidator {
private static final Logger log = LoggerFactory.getLogger(TransactionValidator.class);
Expand Down Expand Up @@ -154,14 +142,25 @@ private boolean hasInvalidTimestamp(TransactionViewModel transactionViewModel) {
snapshotProvider.getInitialSnapshot().getTimestamp(),
snapshotProvider.getInitialSnapshot().hasSolidEntryPoint(transactionViewModel.getHash()));

if (transactionViewModel.getAttachmentTimestamp() == 0) {
if (transactionViewModel.getAttachmentTimestamp() == 0) {
return transactionViewModel.getTimestamp() < snapshotProvider.getInitialSnapshot().getTimestamp() && !snapshotProvider.getInitialSnapshot().hasSolidEntryPoint(transactionViewModel.getHash())
|| transactionViewModel.getTimestamp() > (System.currentTimeMillis() / 1000) + MAX_TIMESTAMP_FUTURE;
}
return transactionViewModel.getAttachmentTimestamp() < (snapshotProvider.getInitialSnapshot().getTimestamp())
|| transactionViewModel.getAttachmentTimestamp() > System.currentTimeMillis() + MAX_TIMESTAMP_FUTURE_MS;
}

private boolean isTransactionRequested(TransactionViewModel transactionViewModel) throws Exception {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The business logic here is not clear. The methods seems to do much more than its name says -- in particular "cancel" the request etc. TransactionValidator at least as its name suggests, should not change the internal state, and handling the requested txs should be a responsibility of TransactionRequester

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There were cases when transactions exist but also they were in requested list, this is why I have added an extra check.

if (transactionRequester.isTransactionRequested(transactionViewModel.getHash(), true)) {
if (TransactionViewModel.exists(tangle, transactionViewModel.getHash())) {
transactionRequester.clearTransactionRequest(transactionViewModel.getHash());
return false;
}
return true;
}
return false;
}

/**
* Runs the following validation checks on a transaction:
* <ol>
Expand All @@ -181,16 +180,23 @@ private boolean hasInvalidTimestamp(TransactionViewModel transactionViewModel) {
public void runValidation(TransactionViewModel transactionViewModel, final int minWeightMagnitude) {
transactionViewModel.setMetadata();
transactionViewModel.setAttachmentData();

try {
if (isTransactionRequested(transactionViewModel)) {
log.error("Waiting for transaction... " + transactionViewModel.getHash());
throw new IllegalStateException("Transaction is requested {} " + transactionViewModel.getHash());
}
} catch (Exception e) {
throw new IllegalStateException("Transaction is requested {} " + transactionViewModel.getHash(), e);
}

if(hasInvalidTimestamp(transactionViewModel)) {
log.debug("Invalid timestamp for txHash/addressHash: {} {}", transactionViewModel.getHash().toString(), transactionViewModel.getAddressHash().toString());
throw new StaleTimestampException("Invalid transaction timestamp.");
}
for (int i = VALUE_OFFSET + VALUE_USABLE_SIZE; i < VALUE_OFFSET + VALUE_SIZE; i++) { // todo always false.
if (transactionViewModel.getBytes()[i] != 0) {
throw new IllegalStateException("Invalid transaction value");
}
if(transactionViewModel.isVirtual()){
return;
}

int weightMagnitude = transactionViewModel.weightMagnitude;
if((weightMagnitude < minWeightMagnitude)) {
throw new IllegalStateException("Invalid transaction hash");
Expand All @@ -211,7 +217,8 @@ public void runValidation(TransactionViewModel transactionViewModel, final int m
* @throws RuntimeException if validation fails
*/
public TransactionViewModel validateBytes(final byte[] bytes, int minWeightMagnitude) {
TransactionViewModel transactionViewModel = new TransactionViewModel(bytes, TransactionHash.calculate(bytes, 0, bytes.length, SpongeFactory.create(SpongeFactory.Mode.S256)));
TransactionViewModel transactionViewModel = new TransactionViewModel(bytes, SpongeFactory.Mode.S256);

runValidation(transactionViewModel, minWeightMagnitude);
return transactionViewModel;
}
Expand Down Expand Up @@ -264,46 +271,38 @@ public boolean checkSolidity(Hash hash, boolean milestone) throws Exception {
* @throws Exception if anything goes wrong while trying to solidify the transaction
*/
public boolean checkSolidity(Hash hash, boolean milestone, int maxProcessedTransactions) throws Exception {
if(fromHash(tangle, hash).isSolid()) {
if (fromHash(tangle, hash).isSolid()) {
return true;
}
Set<Hash> analyzedHashes = new HashSet<>(snapshotProvider.getInitialSnapshot().getSolidEntryPoints().keySet());
if(maxProcessedTransactions != Integer.MAX_VALUE) {
if (maxProcessedTransactions != Integer.MAX_VALUE) {
maxProcessedTransactions += analyzedHashes.size();
}
log.debug("Check solidity for hash " + hash);
boolean solid = true;
final Queue<Hash> nonAnalyzedTransactions = new LinkedList<>(Collections.singleton(hash));
Hash hashPointer;
while ((hashPointer = nonAnalyzedTransactions.poll()) != null) {
if (analyzedHashes.add(hashPointer)) {
if(analyzedHashes.size() >= maxProcessedTransactions) {
if (analyzedHashes.size() >= maxProcessedTransactions) {
return false;
}

final TransactionViewModel transaction = fromHash(tangle, hashPointer);
if(!transaction.isSolid() && !snapshotProvider.getInitialSnapshot().hasSolidEntryPoint(hashPointer)) {
if (!transaction.isSolid() && !snapshotProvider.getInitialSnapshot().hasSolidEntryPoint(hashPointer)) {
if (transaction.getType() == PREFILLED_SLOT) {
solid = false;

if (!transactionRequester.isTransactionRequested(hashPointer, milestone)) {
transactionRequester.requestTransaction(hashPointer, milestone);
solid = false;
break;
}
} else {
// transaction of milestone bundle
TransactionViewModel milestoneTx;
if ((milestoneTx = transaction.isMilestoneBundle(tangle)) != null){
Set<Hash> parents = RoundViewModel.getMilestoneTrunk(tangle, transaction, milestoneTx);
parents.addAll(RoundViewModel.getMilestoneBranch(tangle, transaction, milestoneTx, config.getValidatorSecurity()));
for (Hash parent : parents){
nonAnalyzedTransactions.offer(parent);
}
}
// normal transaction
else {
nonAnalyzedTransactions.offer(transaction.getTrunkTransactionHash());
nonAnalyzedTransactions.offer(transaction.getBranchTransactionHash());
}
nonAnalyzedTransactions.offer(transaction.getTrunkTransactionHash());
log.debug("Check solidity for hash:" + transaction.getHash() + " trunk " + transaction.getTrunkTransactionHash());
nonAnalyzedTransactions.offer(transaction.getBranchTransactionHash());
log.debug("Check solidity for hash:" + transaction.getHash() + " branch " + transaction.getBranchTransactionHash());
}
}
}
Expand Down Expand Up @@ -410,20 +409,12 @@ protected void propagateSolidTransactions() {
//what transaction we gossip.
public void updateStatus(TransactionViewModel transactionViewModel) throws Exception {
transactionRequester.clearTransactionRequest(transactionViewModel.getHash());
if(transactionViewModel.getApprovers(tangle).size() == 0) {

if (transactionViewModel.getApprovers(tangle).size() == 0 && !transactionViewModel.isVirtual()) {
tipsViewModel.addTipHash(transactionViewModel.getHash());
} else {
TransactionViewModel milestoneTx;
if ((milestoneTx = transactionViewModel.isMilestoneBundle(tangle)) != null){
Set<Hash> parents = RoundViewModel.getMilestoneTrunk(tangle, transactionViewModel, milestoneTx);
parents.addAll(RoundViewModel.getMilestoneBranch(tangle, transactionViewModel, milestoneTx, config.getValidatorSecurity()));
for (Hash parent : parents){
tipsViewModel.removeTipHash(parent);
}
} else {
tipsViewModel.removeTipHash(transactionViewModel.getTrunkTransactionHash());
tipsViewModel.removeTipHash(transactionViewModel.getBranchTransactionHash());
}
tipsViewModel.removeTipHash(transactionViewModel.getTrunkTransactionHash());
tipsViewModel.removeTipHash(transactionViewModel.getBranchTransactionHash());
}

if(quickSetSolid(transactionViewModel)) {
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/net/helix/pendulum/conf/BasePendulumConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,11 @@ protected void setSaveLogXMLFile(String saveLogXMLFile) {
this.saveLogXMLFile = saveLogXMLFile;
}

@Override
public double getConfirmationQuorumPercentage() {
return Defaults.CONFIRMATION_QUORUM_PERCENTAGE;
}

public interface Defaults {
//API
int API_PORT = 8085;
Expand Down Expand Up @@ -1059,5 +1064,8 @@ public interface Defaults {
boolean SAVELOG_ENABLED = false;
String SAVELOG_BASE_PATH = "./logs/";
String SAVELOG_XML_FILE = "/logback-save.xml";

//Consensus
double CONFIRMATION_QUORUM_PERCENTAGE = 2.0 / 3.0;
}
}
12 changes: 12 additions & 0 deletions src/main/java/net/helix/pendulum/conf/ConsensusConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,16 @@
* the current code base and will be changed in the future
*/
public interface ConsensusConfig extends SnapshotConfig, MilestoneConfig, ValidatorManagerConfig {

/**
* @return {@value PoWConfig.Descriptions#POW_THREADS}
*/
double getConfirmationQuorumPercentage();

/**
* Field descriptions
*/
interface Descriptions {
String CONFIRMATION_QUORUM_PERCENTAGE = "Confirmation quorum percentage";
}
}
Loading