Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
public interface TransactionRequesterWorker {
/**
* Works through the request queue by sending a request alongside a random tip to each of our neighbors.<br />
*
* @return <code>true</code> when we have send the request to our neighbors, otherwise <code>false</code>
*/
void processRequestQueue();
boolean processRequestQueue();

/**
* Starts the background worker that automatically calls {@link #processRequestQueue()} periodically to process the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class TransactionRequesterWorkerImpl implements TransactionRequesterWorke
/**
* The minimum amount of transactions in the request queue that are required for the worker to trigger.<br />
*/
private static final int REQUESTER_THREAD_ACTIVATION_THRESHOLD = 50;
public static final int REQUESTER_THREAD_ACTIVATION_THRESHOLD = 50;

/**
* The time (in milliseconds) that the worker waits between its iterations.<br />
Expand Down Expand Up @@ -105,24 +105,42 @@ public TransactionRequesterWorkerImpl init(Tangle tangle, TransactionRequester t
* traffic like transactions that get relayed by our node.<br />
*/
@Override
public void processRequestQueue() {
public boolean processRequestQueue() {
try {
if (transactionRequester.numberOfTransactionsToRequest() >= REQUESTER_THREAD_ACTIVATION_THRESHOLD) {
if (isActive()) {
TransactionViewModel transaction = getTransactionToSendWithRequest();
if (transaction != null && transaction.getType() != TransactionViewModel.PREFILLED_SLOT) {
for (Neighbor neighbor : node.getNeighbors()) {
try {
// automatically adds the hash of a requested transaction when sending a packet
node.sendPacket(transaction, neighbor);
} catch (Exception e) {
log.error("unexpected error while sending request to neighbour", e);
}
}
if (isValidTransaction(transaction)) {
sendToNodes(transaction);
return true;
}
}
} catch (Exception e) {
log.error("unexpected error while processing the request queue", e);
}
return false;
}

private void sendToNodes(TransactionViewModel transaction) {
for (Neighbor neighbor : node.getNeighbors()) {
try {
// automatically adds the hash of a requested transaction when sending a packet
node.sendPacket(transaction, neighbor);
} catch (Exception e) {
log.error("unexpected error while sending request to neighbour", e);
}
}
}

//@VisibleForTesting
boolean isActive() {
return transactionRequester.numberOfTransactionsToRequest() >= REQUESTER_THREAD_ACTIVATION_THRESHOLD;
}

//@VisibleForTesting
boolean isValidTransaction(TransactionViewModel transaction) {
return transaction != null && (
transaction.getType() != TransactionViewModel.PREFILLED_SLOT
|| transaction.getHash().equals(Hash.NULL_HASH));
}

@Override
Expand All @@ -145,7 +163,8 @@ public void shutdown() {
* @return a random tip
* @throws Exception if anything unexpected happens while trying to retrieve the random tip.
*/
private TransactionViewModel getTransactionToSendWithRequest() throws Exception {
//@VisibleForTesting
TransactionViewModel getTransactionToSendWithRequest() throws Exception {
Hash tip = tipsViewModel.getRandomSolidTipHash();
if (tip == null) {
tip = tipsViewModel.getRandomNonSolidTipHash();
Expand Down
50 changes: 50 additions & 0 deletions src/test/java/net/helix/hlx/TangleMockUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package net.helix.hlx;

import net.helix.hlx.controllers.TransactionViewModel;
import net.helix.hlx.model.Hash;
import net.helix.hlx.model.persistables.Transaction;
import net.helix.hlx.storage.Tangle;
import net.helix.hlx.utils.Pair;

import org.mockito.Mockito;


public class TangleMockUtils {

/**
* Creates an empty transaction, which is marked filled and parsed.
* This transaction is returned when the hash is asked to load in the tangle object
*
* @param tangle mocked tangle object that shall retrieve a milestone object when being queried for it
* @param hash transaction hash
* @return The newly created (empty) transaction
*/
public static Transaction mockTransaction(Tangle tangle, Hash hash) {
Transaction transaction = new Transaction();
transaction.bytes = new byte[0];
transaction.type = TransactionViewModel.FILLED_SLOT;
transaction.parsed = true;

return mockTransaction(tangle, hash, transaction);
}

/**
* Mocks the tangle object by checking for the hash and returning the transaction.
*
* @param tangle mocked tangle object that shall retrieve a milestone object when being queried for it
* @param hash transaction hash
* @param transaction the transaction we send back
* @return The transaction
*/
public static Transaction mockTransaction(Tangle tangle, Hash hash, Transaction transaction) {
try {
Mockito.when(tangle.load(Transaction.class, hash)).thenReturn(transaction);
Mockito.when(tangle.getLatest(Transaction.class, Hash.class)).thenReturn(new Pair<>(hash, transaction));
} catch (Exception e) {
// the exception can not be raised since we mock
}

return transaction;
}

}
128 changes: 82 additions & 46 deletions src/test/java/net/helix/hlx/TransactionTestUtils.java
Original file line number Diff line number Diff line change
@@ -1,46 +1,82 @@
package net.helix.hlx;

import java.util.Random;

import net.helix.hlx.controllers.TransactionViewModel;
import net.helix.hlx.crypto.SpongeFactory;
import net.helix.hlx.model.Hash;
import net.helix.hlx.model.TransactionHash;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.util.encoders.Hex;

public class TransactionTestUtils {

private static final Random RND = new Random();

/**
* Generates a transaction with the provided hex string.
* Transaction hash is calculated and added.
*
* @param hex The transaction hex to use
* @return The transaction
*/
public static TransactionViewModel createTransactionWithHex(String hex) {
byte[] hbytes = Hex.decode(hex);
return new TransactionViewModel(hbytes, TransactionHash.calculate(SpongeFactory.Mode.S256, hbytes));
}

/**
* @param hex The hex to change.
* @return The changed hex
*/
public static String nextWord(String hex, int index) {
return pad(Integer.toHexString(index+1));
}

private static String pad(String hex) {
return StringUtils.rightPad(hex, TransactionViewModel.SIZE*2, '0');
}

public static Hash getTransactionHash() {
byte[] bytes = new byte[Hash.SIZE_IN_BYTES];
RND.nextBytes(bytes);
return net.helix.hlx.model.HashFactory.TRANSACTION.create(bytes);
}

}
package net.helix.hlx;

import java.util.Random;

import net.helix.hlx.controllers.TransactionViewModel;
import net.helix.hlx.crypto.SpongeFactory;
import net.helix.hlx.model.Hash;
import net.helix.hlx.model.TransactionHash;

import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.util.encoders.Hex;

public class TransactionTestUtils {

private static final Random RND = new Random();

/**
* Generates a transaction with the provided hex string.
* Transaction hash is calculated and added.
*
* @param hex The transaction hex to use
* @return The transaction
*/
public static TransactionViewModel createTransactionWithHex(String hex) {
byte[] hbytes = Hex.decode(hex);
return new TransactionViewModel(hbytes, TransactionHash.calculate(SpongeFactory.Mode.S256, hbytes));
}

/**
* @param hex The hex to change.
* @return The changed hex
*/
public static String nextWord(String hex, int index) {
return pad(Integer.toHexString(index+1));
}

private static String pad(String hex) {
return StringUtils.rightPad(hex, TransactionViewModel.SIZE*2, '0');
}

public static Hash getTransactionHash() {
byte[] bytes = new byte[Hash.SIZE_IN_BYTES];
RND.nextBytes(bytes);
return net.helix.hlx.model.HashFactory.TRANSACTION.create(bytes);
}

/**
* Generates a transaction.
*
* @return The transaction
*/
public static Transaction getTransaction() {
byte[] bytes = new byte[TransactionViewModel.SIZE];
RND.nextBytes(bytes);
return buildTransaction(bytes);
}

/**
* Generates a transaction with only 0s.
*
* @return The transaction
*/
public static Transaction get0Transaction() {
byte[] bytes = new byte[TransactionViewModel.SIZE];
return buildTransaction(bytes);
}

/**
* Builds a transaction from the bytes.
* Make sure the bytes are in the correct order
*
* @param bytes The bytes to build the transaction
* @return The created transaction
*/
public static Transaction buildTransaction(byte[] bytes) {
Transaction t = new Transaction();
t.read(bytes);
t.readMetadata(bytes);
return t;
}

}
Loading