diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Application.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Application.java index 806eae6..b80a6e8 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Application.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/Application.java @@ -145,7 +145,6 @@ public MainChain getMainChain() { */ public void finishTransactionSending() { int nodeID = localStore.getOwnNode().getId(); - //transactionSender.stop(); try { transactionSender.waitUntilDone(); TrackerHelper.setRunning(nodeID, false); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/ProofConstructor.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/ProofConstructor.java index 45a38c4..d8a2961 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/ProofConstructor.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/ProofConstructor.java @@ -22,56 +22,6 @@ public class ProofConstructor { private final Map<Node, List<Block>> toSend; private final Proof proof; - /* - * The Problem: - * We want to send block A. - * We have committed block B. - * With metaknowledge, we can translate this to a list of blocks that need to be sent. - * - * From all these blocks, we have to determine "what is the last block of each node that needs to be sent." - * This is "loop over the blocks, loop over the sources of transactions (recursively): - * - * Block A - * Block B - * Node sender - * Node receiver - * List<Block> ownBlocks = MetaKnowledge.determineBlocks(sender, B) - * processBlocks(sender, ownBlocks) - * Map<Node, List<Block>> toSend - * Map<Node, Set<Integer>> alreadyChecked - * - * processBlocks(Node owner, List<Block> blocks): - * //newlyAdded is the list of all the blocks that were added (not already present) (HAS TO BE ORDERED) - * List<Block> newlyAdded = toSend.addAll(owner, blocks) - * for (Block b : newlyAdded) { - * alreadyChecked.add(owner, b.getNumber()) - * for (Transaction t : b.getTransactions()) { - * processSources(t) - * } - * } - * - * processSources(Transaction t): - * for (Transaction s : t.getSource()) - * Node owner = s.getOwner(); - * //Skip all sources in genesis blocks, our own blocks or in receiver blocks - * if (owner == null || owner == sender || owner == receiver) continue; - * - * Block b = s.getBlock() - * //Skip all blocks that were already checked - * if (b.getNumber() in alreadyChecked.get(owner)) continue; - * - * Block bCom = the first committed block at or after b - * if (bCom.getNumber() in alreadyChecked.get(owner)) continue; - * - * List<Block> blocksOfSource = MetaKnowledge.determineBlocks(owner, bCom) - * if (blocksOfSource is empty) { - * alreadyChecked.addAll(owner, 0 up to (inclusive) bCom.getNumber()) - * continue; - * } - * - * processBlocks(owner, blocksOfSource) - */ - /** * @param mainTransaction - the transaction to construct the proof for */ diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java index 546b800..85f7d2d 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/SimulationMain.java @@ -6,7 +6,7 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.model.OwnNode; import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.Simulation; import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.tendermint.TendermintHelper; -import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.transactionpattern.RandomTransactionPattern; +import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.transactionpattern.ITransactionPattern; import nl.tudelft.blockchain.scaleoutdistributedledger.simulation.transactionpattern.UniformRandomTransactionPattern; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Utils; @@ -34,18 +34,22 @@ public final class SimulationMain { //Whether this main is the master coordinator of the simulation //Note that the master should always be started first public static final boolean IS_MASTER = true; - //The duration of the simulation in seconds. Only has an effect when IS_MASTER == true. + //The duration of the simulation in seconds. public static final int SIMULATION_DURATION = 600; - // Maximum number of blocks waiting to be sent (no new transaction created in the mean time + //Maximum number of blocks waiting to be sent (no new transaction will be created in the mean time) public static final int MAX_BLOCKS_PENDING = 50; - // The initial delay in milliseconds to wait before checking for the first time. + //The initial delay in milliseconds to wait before checking what blocks can be sent. public static final long INITIAL_SENDING_DELAY = 5000; - // The time in milliseconds to wait before checking again. + //The time in milliseconds between send checks. public static final long SENDING_WAIT_TIME = 5000; - // The number of blocks (with the same or higher block number) that need to be committed before we send a certain block. + //The number of blocks (with the same or higher block number) that need to be committed before we send a certain block. public static final int REQUIRED_COMMITS = 2; // The number of transactions that are registered in one batch. public static final int REGISTER_TRANSACTIONS_EVERY = 10; + //The transaction pattern that is used. + public static final ITransactionPattern TRANSACTION_PATTERN = new UniformRandomTransactionPattern(10, 20, 100, 200, 10); + //The initial amount of money each node has. + public static final long INITIAL_MONEY = 1000000; private SimulationMain() {} @@ -76,9 +80,8 @@ public static void main(String[] args) throws Exception { // --- PHASE 2: all nodes registered, so create genesis block and genesis.json files --- //update nodes from the tracker - Map<Integer, Node> nodes = new HashMap<>(TOTAL_NODES_NUMBER); - TrackerHelper.updateNodes(nodes, null); - final Block genesisBlock = TendermintHelper.generateGenesisBlock(1000000, nodes); + Map<Integer, Node> nodes = TrackerHelper.getNodes(); + Block genesisBlock = TendermintHelper.generateGenesisBlock(INITIAL_MONEY, nodes); //generate genesis.json for all local nodes TendermintHelper.generateGenesisFiles(new Date(), @@ -88,10 +91,7 @@ public static void main(String[] args) throws Exception { // --- PHASE 3: start the actual simulation --- Simulation simulation = new Simulation(IS_MASTER); - - RandomTransactionPattern rtp = new UniformRandomTransactionPattern(10, 20, 100, 200, 10); -// rtp.setSeed(1); - simulation.setTransactionPattern(rtp); + simulation.setTransactionPattern(TRANSACTION_PATTERN); simulation.runNodesLocally(nodes, ownNodes, genesisBlock, nodeToKeyPair); // Wait for all nodes to have initialized @@ -104,13 +104,9 @@ public static void main(String[] args) throws Exception { // --- PHASE 4: stop the simulation --- + Thread.sleep(SIMULATION_DURATION * 1000); - if (IS_MASTER) { - Thread.sleep(SIMULATION_DURATION * 1000); - } - - // Wait for all the other nodes to stop - + //Stop the simulation and wait for nodes to stop. simulation.stop(); waitForStop(); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java index 0153248..456fc71 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TrackerHelper.java @@ -23,252 +23,290 @@ import java.net.SocketException; import java.net.UnknownHostException; import java.util.Enumeration; -import java.util.LinkedList; +import java.util.HashMap; import java.util.Map; import java.util.Queue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; import java.util.logging.Level; /** * Helper class for interacting with the tracker. */ public final class TrackerHelper { - private volatile static Queue<TransactionRegistration> transactionsToBeRegistered = new LinkedList<>(); - private static final CloseableHttpClient client = HttpClientBuilder.create().build(); + private static final String TRACKER_URL = String.format("http://%s:%d", Application.TRACKER_SERVER_ADDRESS, Application.TRACKER_SERVER_PORT); - private TrackerHelper() { - throw new UnsupportedOperationException(); - } + private static volatile Queue<TransactionRegistration> transactionsToRegister = new LinkedBlockingQueue<>(); + private static final CloseableHttpClient CLIENT = HttpClientBuilder.create().build(); + private static final ExecutorService POOL = Executors.newSingleThreadExecutor(); - /** - * Reset the tracker server with a new empty nodelist. - * @return - boolean identifying if the reset was successful - * @throws IOException - exception while resetting tracker server - */ - public static boolean resetTrackerServer() throws IOException { - try (CloseableHttpClient client = HttpClientBuilder.create().build()) { - HttpPost request = new HttpPost(String.format("http://%s:%d/reset", Application.TRACKER_SERVER_ADDRESS, Application.TRACKER_SERVER_PORT)); - JSONObject response = new JSONObject(IOUtils.toString(client.execute(request).getEntity().getContent())); - if (response.getBoolean("success")) { - Log.log(Level.INFO, "Successfully resetted the tracker server"); - return true; - } - Log.log(Level.SEVERE, "Error while resetting the tracker server"); - return false; - } - } + private TrackerHelper() { + throw new UnsupportedOperationException(); + } - /** - * Registers this node with the given public key. - * @param nodePort - the port of the node - * @param publicKey - the publicKey of the new node - * @param id - the id of the node - * @return - the registered node - * @throws IOException - IOException while registering node - * @throws NodeRegisterFailedException - Server side exception while registering node - */ - public static OwnNode registerNode(int nodePort, byte[] publicKey, int id) throws IOException, NodeRegisterFailedException { - String address = getIP(); - JSONObject json = new JSONObject(); - json.put("address", address); - json.put("port", nodePort); - json.put("publicKey", publicKey); - json.put("id", id); + /** + * Get the status of the tracker. + * @return - a JSON object describing the status of the tracker + * @throws IOException - if the connection to the tracker server cannot be made + */ + public static JSONObject getStatus() throws IOException { + return getToTracker("/status"); + } - try (CloseableHttpClient client = HttpClientBuilder.create().build()) { - StringEntity requestEntity = new StringEntity(json.toString(), ContentType.APPLICATION_JSON); - HttpPost request = new HttpPost(String.format("http://%s:%d/register-node", Application.TRACKER_SERVER_ADDRESS, Application.TRACKER_SERVER_PORT)); - request.setEntity(requestEntity); - JSONObject response = new JSONObject(IOUtils.toString(client.execute(request).getEntity().getContent())); - if (response.getBoolean("success")) { - Log.log(Level.INFO, "Successfully registered node to tracker server"); - return new OwnNode(id, publicKey, address, nodePort); - } - Log.log(Level.SEVERE, "Error while registering node"); - throw new NodeRegisterFailedException(); - } - } + /** + * Get the number of running nodes from the tracker. + * @return - the number of nodes already registered in tracker + * @throws IOException - if the connection to the tracker server cannot be made + */ + public static int getRunning() throws IOException { + return getStatus().getInt("running"); + } + + /** + * Mark a node with the given id as initialized on the tracker. + * @param id - the id of the node to mark + * @param running - if the node is running or not + * @throws IOException - IOException while registering node + * @throws TrackerException - Server side exception while updating running status + */ + public static void setRunning(int id, boolean running) throws IOException, TrackerException { + JSONObject json = new JSONObject(); + json.put("id", id); + json.put("running", running); - /** - * Mark a node with the given id as initialized on the tracker. - * @param id - the id of the node to mark - * @param running - if the node is running or not - * @throws IOException - IOException while registering node - * @throws TrackerException - Server side exception while updating running status - */ - public static void setRunning(int id, boolean running) throws IOException, TrackerException { - JSONObject json = new JSONObject(); - json.put("id", id); - json.put("running", running); + if (postToTracker("/set-node-status", json)) { + Log.log(Level.INFO, "Successfully updated node " + id + " to running=" + running); + return; + } - try (CloseableHttpClient client = HttpClientBuilder.create().build()) { - StringEntity requestEntity = new StringEntity(json.toString(), ContentType.APPLICATION_JSON); - HttpPost request = new HttpPost(String.format("http://%s:%d/set-node-status", Application.TRACKER_SERVER_ADDRESS, Application.TRACKER_SERVER_PORT)); - request.setEntity(requestEntity); - JSONObject response = new JSONObject(IOUtils.toString(client.execute(request).getEntity().getContent())); - if (response.getBoolean("success")) { - Log.log(Level.INFO, "Successfully updated node " + id + " to running=" + running); - return; - } - Log.log(Level.SEVERE, "Error while updating the running status of the node"); - throw new TrackerException("Unable to update to running."); - } - } + Log.log(Level.SEVERE, "Error while updating the running status of the node"); + throw new TrackerException("Unable to update to running."); + } - /** - * Tries to resolve the IP(v4) address of this machine. - * When it fails to do so it uses the local IP. - * - * @return the IP of this machine - */ - public static String getIP() { - try { - Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); - while (interfaces.hasMoreElements()) { - NetworkInterface ni = interfaces.nextElement(); - Enumeration<InetAddress> addrss = ni.getInetAddresses(); - while (addrss.hasMoreElements()) { - String addr = addrss.nextElement().getHostAddress(); - if (addr.contains(":") || addr.startsWith("127.")) continue; // IPv6 or Local - return addr; - } - } - } catch (SocketException e) { } // Intentionally empty catch block - try { - Log.log(Level.WARNING, "Could not resolve address, using localhost instead"); - return InetAddress.getLocalHost().getHostAddress(); - } catch (UnknownHostException e) { - Log.log(Level.SEVERE, "Could not resolve localhost address, please check your network configuration"); - return "0.0.0.0"; - } - } + /** + * Get the number of registered nodes from the tracker. + * @return - the number of nodes already registered in tracker + * @throws IOException - if the connection to the tracker server cannot be made + */ + public static int getRegistered() throws IOException { + return getStatus().getInt("registered"); + } + + /** + * Registers this node with the given public key. + * @param nodePort - the port of the node + * @param publicKey - the publicKey of the new node + * @param id - the id of the node + * @return - the registered node + * @throws IOException - if the connection to the tracker server cannot be made + * @throws NodeRegisterFailedException - Server side exception while registering node + */ + public static OwnNode registerNode(int nodePort, byte[] publicKey, int id) throws IOException, NodeRegisterFailedException { + String address = getIP(); + JSONObject json = new JSONObject(); + json.put("address", address); + json.put("port", nodePort); + json.put("publicKey", publicKey); + json.put("id", id); - /** - * Updates the given map of nodes with new information from the tracker. - * @param nodes - the map of nodes - * @throws IOException - exception while updating nodes - */ - public static void updateNodes(Map<Integer, Node> nodes, OwnNode ownNode) throws IOException { - try (CloseableHttpClient client = HttpClientBuilder.create().build()) { - HttpGet request = new HttpGet(String.format("http://%s:%d", Application.TRACKER_SERVER_ADDRESS, Application.TRACKER_SERVER_PORT)); - JSONArray nodesArray = (JSONArray) new JSONObject(IOUtils.toString(client.execute(request).getEntity().getContent())).get("nodes"); + if (postToTracker("/register-node", json)) { + Log.log(Level.INFO, "Successfully registered node to tracker server"); + return new OwnNode(id, publicKey, address, nodePort); + } - for (int i = 0; i < nodesArray.length(); i++) { - JSONObject object = (JSONObject) nodesArray.get(i); - byte[] publicKey = jsonArrayToByteArray((JSONArray) object.get("publicKey")); - String address = object.getString("address"); - int port = object.getInt("port"); - if (nodes.containsKey(i)) { - Node node = nodes.get(i); - node.setAddress(address); - node.setPort(port); - } else { - Node node = new Node(i, publicKey, address, port); + Log.log(Level.SEVERE, "Error while registering node"); + throw new NodeRegisterFailedException(); + } - //TODO Check if we need this. - if (ownNode != null) { - node.getChain().setGenesisBlock(ownNode.getChain().getGenesisBlock()); - } + /** + * Reset the tracker server with a new empty nodelist. + * @return - boolean identifying if the reset was successful + * @throws IOException - if the connection to the tracker server cannot be made + */ + public static boolean resetTrackerServer() throws IOException { + if (postToTracker("/reset", null)) { + Log.log(Level.INFO, "Successfully reset the tracker server"); + return true; + } - nodes.put(i, node); - } - } - } - } + Log.log(Level.SEVERE, "Error while resetting the tracker server"); + return false; + } + + /** + * Gets the nodes that are registered with the tracker. + * @return - the nodes that are registered with the tracker. + * @throws IOException - exception while getting nodes + */ + public static Map<Integer, Node> getNodes() throws IOException { + return updateNodes(new HashMap<>(), null); + } - /** - * Get the status of the tracker. - * @return a JSON object describing the status of the tracker - * @throws IOException when problems with creating/closing http client - */ - public static JSONObject getStatus() throws IOException { - try (CloseableHttpClient client = HttpClientBuilder.create().build()) { - HttpGet request = new HttpGet(String.format("http://%s:%d/status", Application.TRACKER_SERVER_ADDRESS, Application.TRACKER_SERVER_PORT)); - return new JSONObject(IOUtils.toString(client.execute(request).getEntity().getContent())); - } - } + /** + * Updates the given map of nodes with new information from the tracker. + * @param nodes - the map of nodes + * @param ownNode - our own node (or null) + * @return - the given map + * @throws IOException - exception while updating nodes + */ + public static Map<Integer, Node> updateNodes(Map<Integer, Node> nodes, OwnNode ownNode) throws IOException { + JSONArray nodesArray = getToTracker("/").getJSONArray("nodes"); - /** - * Get the number of running nodes from the tracker. - * @return the number of nodes already registered in tracker - * @throws IOException when problems with creating/closing http client - */ - public static int getRunning() throws IOException { - return getStatus().getInt("running"); - } + for (int i = 0; i < nodesArray.length(); i++) { + JSONObject object = nodesArray.getJSONObject(i); + byte[] publicKey = jsonArrayToByteArray(object.getJSONArray("publicKey")); + String address = object.getString("address"); + int port = object.getInt("port"); + if (nodes.containsKey(i)) { + Node node = nodes.get(i); + node.setAddress(address); + node.setPort(port); + } else { + Node node = new Node(i, publicKey, address, port); + nodes.put(i, node); + } + } + + return nodes; + } - /** - * Get the number of registered nodes from the tracker. - * @return the number of nodes already registered in tracker - * @throws IOException when problems with creating/closing http client - */ - public static int getRegistered() throws IOException { - return getStatus().getInt("registered"); - } + /** + * Registers a transaction to a queue, ready to be send to the server. + * @param proof - the proof used to send the transaction. + */ + public static void registerTransaction(Proof proof) { + transactionsToRegister.add(new TransactionRegistration(proof.getTransaction(), proof.getChainUpdates().size(), proof.getNumberOfBlocks())); - /** - * Converts JSONArray containing ints to byte array. - * @param json - the jsonarray to convert. - * @return - the generated byte array - */ - private static byte[] jsonArrayToByteArray(JSONArray json) { - byte[] res = new byte[json.length()]; - for (int i = 0; i < json.length(); i++) { - res[i] = (byte) json.getInt(i); - } - return res; - } + //Check if we should send both before and in the synchronized block, to prevent blocking. + if (transactionsToRegister.size() < SimulationMain.REGISTER_TRANSACTIONS_EVERY) return; - /** - * Registers a transaction to a queue, ready to be send to the server. - * @param proof - the proof used to send the transaction. - */ - public static synchronized void registerTransaction(Proof proof) { - transactionsToBeRegistered.add(new TransactionRegistration( - proof.getTransaction(), proof.getChainUpdates().size(), proof.getNumberOfBlocks())); + synchronized (TrackerHelper.class) { + if (transactionsToRegister.size() < SimulationMain.REGISTER_TRANSACTIONS_EVERY) return; - try { - Queue<TransactionRegistration> transactionsToBeSend; - synchronized (TrackerHelper.class) { - if (transactionsToBeRegistered.size() < SimulationMain.REGISTER_TRANSACTIONS_EVERY) return; - transactionsToBeSend = transactionsToBeRegistered; - transactionsToBeRegistered = new LinkedList<>(); - } - sendTransactions(transactionsToBeSend); - } catch (IOException e) { - Log.log(Level.WARNING, "Transaction registration failed", e); - } - } + //Swap the queue out with a new one + Queue<TransactionRegistration> toSend = transactionsToRegister; + transactionsToRegister = new LinkedBlockingQueue<>(); - /** - * Actually send the transactions to the server. - * @throws IOException - exception while sending. - */ - private static synchronized void sendTransactions(Queue<TransactionRegistration> transactionsToBeSend) throws IOException { - JSONArray transactionArray = new JSONArray(); + //Send the transactions + POOL.submit(() -> { + try { + sendTransactions(toSend); + } catch (IOException e) { + Log.log(Level.WARNING, "Transaction registration failed", e); + } + }); + } + } - while (!transactionsToBeSend.isEmpty()) { - TransactionRegistration next = transactionsToBeSend.poll(); - JSONObject json = new JSONObject(); - json.put("from", next.getTransaction().getSender().getId()); - json.put("to", next.getTransaction().getReceiver().getId()); - json.put("amount", next.getTransaction().getAmount()); - json.put("remainder", next.getTransaction().getRemainder()); - json.put("numberOfChains", next.getNumberOfChains()); - json.put("numberOfBlocks", next.getNumberOfBlocks()); - transactionArray.put(json); - } - JSONObject json = new JSONObject(); - json.put("transactions", transactionArray); + /** + * Sends the given transactions to the server. + * @param toSend - the transactions to register + * @throws IOException - exception while sending. + */ + private static void sendTransactions(Queue<TransactionRegistration> toSend) throws IOException { + JSONArray transactionArray = new JSONArray(); - StringEntity requestEntity = new StringEntity(json.toString(), ContentType.APPLICATION_JSON); - HttpPost request = new HttpPost(String.format("http://%s:%d/register-transactions", - Application.TRACKER_SERVER_ADDRESS, Application.TRACKER_SERVER_PORT)); - request.setEntity(requestEntity); - JSONObject response = new JSONObject(IOUtils.toString(client.execute(request).getEntity().getContent())); - if (response.getBoolean("success")) { - Log.log(Level.INFO, "Successfully registered " + transactionArray.length() + " transactions to tracker server"); - } else { - Log.log(Level.WARNING, "Error while registering transactions"); - } - } + for (TransactionRegistration next : toSend) { + JSONObject json = new JSONObject(); + json.put("from", next.getTransaction().getSender().getId()); + json.put("to", next.getTransaction().getReceiver().getId()); + json.put("amount", next.getTransaction().getAmount()); + json.put("remainder", next.getTransaction().getRemainder()); + json.put("numberOfChains", next.getNumberOfChains()); + json.put("numberOfBlocks", next.getNumberOfBlocks()); + transactionArray.put(json); + } + JSONObject json = new JSONObject(); + json.put("transactions", transactionArray); + + StringEntity requestEntity = new StringEntity(json.toString(), ContentType.APPLICATION_JSON); + HttpPost request = new HttpPost(TRACKER_URL + "/register-transactions"); + request.setEntity(requestEntity); + JSONObject response = new JSONObject(IOUtils.toString(CLIENT.execute(request).getEntity().getContent())); + if (response.getBoolean("success")) { + Log.log(Level.INFO, "Successfully registered " + transactionArray.length() + " transactions to tracker server"); + } else { + Log.log(Level.WARNING, "Error while registering transactions"); + } + } + + /** + * Makes a POST request to the tracker server and returns its success. + * @param endPoint - the endpoint to post to + * @param json - the json to send (can be null) + * @return - true if the json response had success = true, false otherwise + * @throws IOException - If there was an error while connecting to the tracker server. + */ + public static boolean postToTracker(String endPoint, JSONObject json) throws IOException { + try (CloseableHttpClient client = HttpClientBuilder.create().build()) { + HttpPost request = new HttpPost(TRACKER_URL + endPoint); + + if (json != null) { + StringEntity requestEntity = new StringEntity(json.toString(), ContentType.APPLICATION_JSON); + request.setEntity(requestEntity); + } + + JSONObject response = new JSONObject(IOUtils.toString(client.execute(request).getEntity().getContent())); + return response.getBoolean("success"); + } + } + + /** + * Makes a GET request to the tracker server and returns the result as a JSONObject. + * @param endPoint - the endpoint to get + * @return - the json returned by the tracker + * @throws IOException - If there was an error while connecting to the tracker server. + */ + public static JSONObject getToTracker(String endPoint) throws IOException { + try (CloseableHttpClient client = HttpClientBuilder.create().build()) { + HttpGet request = new HttpGet(TRACKER_URL + endPoint); + return new JSONObject(IOUtils.toString(client.execute(request).getEntity().getContent())); + } + } + + /** + * Tries to resolve the IP(v4) address of this machine. + * When it fails to do so it uses the local IP. + * + * @return the IP of this machine + */ + public static String getIP() { + try { + Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); + while (interfaces.hasMoreElements()) { + NetworkInterface ni = interfaces.nextElement(); + Enumeration<InetAddress> addresses = ni.getInetAddresses(); + while (addresses.hasMoreElements()) { + String address = addresses.nextElement().getHostAddress(); + //IPv6, Local or docker + if (address.contains(":") || address.startsWith("127.") || address.startsWith("172.")) continue; + return address; + } + } + } catch (SocketException e) { + // Intentionally empty catch block + } + + try { + Log.log(Level.WARNING, "Could not resolve address, using localhost instead"); + return InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + Log.log(Level.SEVERE, "Could not resolve localhost address, please check your network configuration"); + return "0.0.0.0"; + } + } + + /** + * Converts JSONArray containing ints to byte array. + * @param json - the jsonarray to convert. + * @return - the generated byte array + */ + private static byte[] jsonArrayToByteArray(JSONArray json) { + byte[] res = new byte[json.length()]; + for (int i = 0; i < json.length(); i++) { + res[i] = (byte) json.getInt(i); + } + return res; + } } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java index e23db34..fda5cad 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionCreator.java @@ -29,7 +29,6 @@ public class TransactionCreator { private final Node sender; private final Node receiver; private final long amount; - //private final BitSet known; private int currentBest = Integer.MAX_VALUE; private TransactionTuple currentBestTuple; @@ -45,29 +44,8 @@ public TransactionCreator(LocalStore localStore, Node receiver, long amount) { this.sender = localStore.getOwnNode(); this.receiver = receiver; this.amount = amount; - //this.known = calculateKnowledge(); } -//TODO IMPORTANT Determine if the current transaction creation method is correct. -// /** -// * Calculates what the receiver already knows about. -// * -// * @return a bitset with the chains the receiver already knows about -// */ -// private BitSet calculateKnowledge() { -// synchronized (receiver.getMetaKnowledge()) { -// BitSet collected = receiver.getMetaKnowledge() -// .keySet() -// .stream() -// .collect(() -> new BitSet(nodesCount), -// (bs, i) -> bs.set(i), -// (bs1, bs2) -> bs1.or(bs2) -// ); -// -// return collected; -// } -// } - /** * Creates a transaction. * diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java index 55445ae..12ef5b4 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/TransactionSender.java @@ -56,14 +56,6 @@ public void run() { * Sends all blocks that can be sent. */ public void sendAllBlocksThatCanBeSent() { - //TODO Add explanation in readme? - //[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - //Committed: [0, 2, 4, 6, 8] - //Last sent: 2 - //Committed found: [3 -> 4, 5 -> 6, 7 -> 8] = [4, 6, 8] - //We can send up to 6 (inclusive) - //3 commits found, 2 required - int lastBlockNr = chain.getLastBlockNumber(); //Determine what blocks have been committed since the last batch we sent @@ -149,15 +141,16 @@ private boolean sendTransaction(Transaction transaction) throws InterruptedExcep long startingTime = System.currentTimeMillis(); Node to = transaction.getReceiver(); - //TODO IMPORTANT Removed synchronization ProofConstructor proofConstructor = new ProofConstructor(transaction); Proof proof = proofConstructor.constructProof(); ProofMessage msg = new ProofMessage(proof); + //Check if the proof creation took a long time and log it. long timeDelta = System.currentTimeMillis() - startingTime; if (timeDelta > 5 * 1000) { Log.log(Level.WARNING, "Proof creation took " + timeDelta + " ms for transaction: " + transaction); } + Log.log(Level.FINE, "Node " + transaction.getSender().getId() + " now actually sending transaction: " + transaction); if (socketClient.sendMessage(to, msg)) { to.updateMetaKnowledge(proof); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/NodeStateChangeFailedException.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/NodeStateChangeFailedException.java similarity index 76% rename from src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/NodeStateChangeFailedException.java rename to src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/NodeStateChangeFailedException.java index c673af5..288a2e2 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/NodeStateChangeFailedException.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/exceptions/NodeStateChangeFailedException.java @@ -1,11 +1,13 @@ -package nl.tudelft.blockchain.scaleoutdistributedledger; +package nl.tudelft.blockchain.scaleoutdistributedledger.exceptions; /** * Exception for indicating that updating the running state of a node on the tracker failed. */ public class NodeStateChangeFailedException extends RuntimeException { + private static final long serialVersionUID = 1L; + /** - * Constructor + * Constructor. * @param id - the ID of the node for which it failed * @param running - the running state that it could not be updated to */ diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/BlockMessage.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/BlockMessage.java index 32ae5e4..63ee73c 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/BlockMessage.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/BlockMessage.java @@ -10,22 +10,18 @@ import java.util.ArrayList; import java.util.List; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; - /** * Block message for netty. */ public class BlockMessage extends Message { - + private static final long serialVersionUID = 1L; + @Getter private final int number; @Getter private final int previousBlockNumber; - //@Getter - //private final BlockMessage previousBlock; - @Getter private final int ownerId; @@ -34,16 +30,12 @@ public class BlockMessage extends Message { @Getter private final Sha256Hash hash; - - //private transient BlockAbstract blockAbstract; - //private transient Boolean hasAbstract; /** * Constructor. * @param block - original block - * @param proofReceiver - receiver of the proof */ - public BlockMessage(Block block, Node proofReceiver) { + public BlockMessage(Block block) { this.number = block.getNumber(); Block prevBlock = block.getPreviousBlock(); if (prevBlock != null) { @@ -60,7 +52,7 @@ public BlockMessage(Block block, Node proofReceiver) { } this.transactions = new ArrayList<>(); for (Transaction transaction : block.getTransactions()) { - this.transactions.add(new TransactionMessage(transaction, proofReceiver)); + this.transactions.add(new TransactionMessage(transaction)); } this.hash = block.getHash(); } @@ -70,9 +62,13 @@ public void handle(LocalStore localStore) { // Do nothing. } + /** + * @param localStore - the local store + * @return - the block that this message represents, without any sources in the transactions + */ public Block toBlockWithoutSources(LocalStore localStore) { List<Transaction> transactions = new ArrayList<>(); - for(TransactionMessage tm : this.transactions) { + for (TransactionMessage tm : this.transactions) { transactions.add(tm.toTransactionWithoutSources(localStore)); } return new Block(this.number, localStore.getNode(this.ownerId), transactions); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/ProofMessage.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/ProofMessage.java index 52740f8..af0438b 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/ProofMessage.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/ProofMessage.java @@ -38,8 +38,7 @@ public class ProofMessage extends Message { * @param proof - original proof */ public ProofMessage(Proof proof) { - Node proofReceiver = proof.getTransaction().getReceiver(); - this.transactionMessage = new TransactionMessage(proof.getTransaction(), proofReceiver); + this.transactionMessage = new TransactionMessage(proof.getTransaction()); this.chainUpdates = new HashMap<>(); for (Entry<Node, List<Block>> entry : proof.getChainUpdates().entrySet()) { Node node = entry.getKey(); @@ -49,7 +48,7 @@ public ProofMessage(Proof proof) { List<BlockMessage> blockMessageList = new ArrayList<>(); for (int i = 0; i < blockList.size(); i++) { Block block = blockList.get(i); - blockMessageList.add(new BlockMessage(block, proofReceiver)); + blockMessageList.add(new BlockMessage(block)); } this.chainUpdates.put(node.getId(), blockMessageList); } @@ -58,7 +57,6 @@ public ProofMessage(Proof proof) { @Override public void handle(LocalStore localStore) { - //TODO IMPORTANT Removed synchronized on own chain try { CommunicationHelper.receiveTransaction(new Proof(this, localStore), localStore); } catch (IOException e) { diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java index d3b2dde..c4a67a5 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/message/TransactionMessage.java @@ -40,9 +40,8 @@ public class TransactionMessage extends Message { /** * Constructor. * @param transaction - the original transaction object - * @param proofReceiver - receiver of the proof */ - public TransactionMessage(Transaction transaction, Node proofReceiver) { + public TransactionMessage(Transaction transaction) { if (!transaction.getBlockNumber().isPresent()) { throw new RuntimeException("Block number not present"); } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/mocks/TendermintChainMock.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/mocks/TendermintChainMock.java index 2bb29ca..2141027 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/mocks/TendermintChainMock.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/mocks/TendermintChainMock.java @@ -37,6 +37,11 @@ public Sha256Hash commitAbstract(BlockAbstract abs) { return Sha256Hash.withHash(hash); } + @Override + public boolean isPresent(Sha256Hash hash) { + return true; + } + @Override public boolean isPresent(Block block) { return true; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java index 6c87717..3d679f1 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Block.java @@ -192,7 +192,7 @@ public boolean equals(Object obj) { if (other.owner != null) return false; } else if (!this.owner.equals(other.owner)) return false; - //TODO IMPORTANT We might not want to use equals for the previous block (as it will recurse further) + //TODO We might not want to use equals for the previous block (as it will recurse further) if (this.previousBlock == null) { if (other.previousBlock != null) return false; } else if (!this.previousBlock.equals(other.previousBlock)) return false; @@ -216,14 +216,12 @@ private Sha256Hash calculateHash() { // Important to keep the order of writings outputStream.write(Utils.intToByteArray(this.number)); - //TODO IMPORTANT Should we include the hash of the previous block? byte[] prevBlockHash = (this.previousBlock != null) ? this.previousBlock.getHash().getBytes() : new byte[0]; outputStream.write(prevBlockHash); if (this.owner != null) { outputStream.write(Utils.intToByteArray(this.owner.getId())); } - //TODO IMPORTANT Remove the transactions for testing for (Transaction tx : this.transactions) { outputStream.write(tx.getHash().getBytes()); } @@ -261,7 +259,6 @@ public Block genesisCopy() { * @return - boolean identifying if an abstract of this block is on the main chain. */ public boolean isOnMainChain(LocalStore localStore) { - //TODO Remove hack? if (this.number == GENESIS_BLOCK_NUMBER) return true; //Definitely has no abstract diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java index 6cdb768..21e5b5f 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Chain.java @@ -80,28 +80,6 @@ public void update(List<Block> updates, LocalStore localStore) { setLastCommittedBlock(lastCommitted); } - /** - * Fixes the next committed block pointers. - * @param updates - the block updates - * @param localStore - the local store - */ - private void fixNextCommitted(List<Block> updates, LocalStore localStore) { - if (updates.isEmpty()) return; - - int lastBlockNr = updates.get(0).getNumber(); - for (Block block : updates) { - if (!localStore.getMainChain().isInCache(block)) continue; - - Block prev = block.getPreviousBlock(); - while (prev != null && prev.getNumber() > lastBlockNr) { - prev.setNextCommittedBlock(block); - prev = prev.getPreviousBlock(); - } - - lastBlockNr = block.getNumber(); - } - } - /** * @param block - the block * @param lastUpdateBlockNr - the number of the last block in the list of updates diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java index 68d938c..eecd63e 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ChainView.java @@ -11,7 +11,7 @@ */ public class ChainView implements Iterable<Block> { - //TODO If the chain is updated after validation, the validation needs to be done again. if (valid) then valid = null; + //NOTE If the chain is updated after validation, the validation needs to be done again. private Chain chain; private List<Block> updates; private Boolean valid; @@ -102,7 +102,6 @@ private boolean checkIntegrity() { int lastOwnNumber = chainSize - 1; if (firstUpdateNumber - lastOwnNumber > 1) { //We are missing blocks between what we know and what we were sent! Can never happen with an honest node. - System.out.println("HERE1"); this.valid = false; return false; } else if (lastOwnNumber >= firstUpdateNumber) { @@ -113,8 +112,6 @@ private boolean checkIntegrity() { for (int i = 0; i < overlap && !updates.isEmpty() && startIndex < updates.size(); i++) { Block ownBlock = chain.getBlocks().get(baseI + i); Block updatedBlock = updates.get(startIndex); - - //TODO we might need a special equality check if (!ownBlock.equals(updatedBlock)) { this.valid = false; return false; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java index b3220f5..b0954c4 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Proof.java @@ -91,7 +91,7 @@ private void fixPreviousBlockPointers() { private void fixTransactionSources(LocalStore localStore) { HashMap<Integer, LightView> lightViews = new HashMap<>(); - // Initialize the chainviews only once + // Initialize the lightviews only once for (Node node : this.chainUpdates.keySet()) { lightViews.put(node.getId(), new LightView(node.getChain(), chainUpdates.get(node))); } @@ -218,10 +218,6 @@ private void verifySourceTransactions(Transaction transaction, LocalStore localS try { verify(sourceTransaction, localStore); } catch (ValidationException ex) { - //TODO Remove debugging stuff - ex.printStackTrace(); - System.out.println(this); - System.exit(1); throw new ProofValidationException("Source " + sourceTransaction + " is not valid", ex); } } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Sha256Hash.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Sha256Hash.java index e27e47a..2e82a3b 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Sha256Hash.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Sha256Hash.java @@ -15,7 +15,8 @@ * Class to wrap a SHA256 hash. */ public class Sha256Hash implements Serializable { - + private static final long serialVersionUID = 1L; + @Getter private byte[] bytes; diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java index bf7ba00..7a831c5 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/Transaction.java @@ -84,8 +84,7 @@ public Transaction(int number, Node sender, Node receiver, long amount, long rem /** * Returns the number of the block (if it is in a block). - * TODO: maybe do this more efficiently (when adding the transaction to the local chain or something) - * @return - optional that is empty if the transaction is not in a block, and filled with the number of the block otherwise. + * @return - the number of the block that this transaction is in, if known */ public OptionalInt getBlockNumber() { if (!this.blockNumber.isPresent()) { @@ -93,8 +92,6 @@ public OptionalInt getBlockNumber() { if (this.sender == null) { this.blockNumber = OptionalInt.of(Block.GENESIS_BLOCK_NUMBER); } else { - //TODO IMPORTANT We don't want this to be called really. - System.out.println("Looking up block number!"); for (Block block : sender.getChain().getBlocks()) { if (block.getTransactions().contains(this)) { this.blockNumber = OptionalInt.of(block.getNumber()); diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/MainChain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/MainChain.java index 0d7a0c4..76e59ce 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/MainChain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/MainChain.java @@ -16,6 +16,13 @@ public interface MainChain { */ public Sha256Hash commitAbstract(BlockAbstract abs); + /** + * Check whether the given hash is on the main chain (in a form of BlockAbstract). + * @param hash the hash of the block to check + * @return true if there is a block abstract of the given hash, false otherwise. + */ + public boolean isPresent(Sha256Hash hash); + /** * Check whether the block (from a local chain) is on the main chain (in a form of BlockAbstract). * @param block the block to check diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIClient.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIClient.java index 5339267..5fe23e9 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIClient.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIClient.java @@ -44,15 +44,18 @@ public byte[] commit(BlockAbstract abs) { Log.log(Level.INFO, "Could not commit the abstract because: " + error.getString("data")); Log.log(Level.INFO, result.toString()); return null; - } else { //No error coming from Tendermint found + } else { + //No error coming from Tendermint found byte[] ret = null; try { JSONObject resultField = result.getJSONObject("result"); JSONObject deliverTx = resultField.getJSONObject("deliver_tx"); - if (deliverTx.getInt("code") == 0) { //double check we succeeded + if (deliverTx.getInt("code") == 0) { + //double check we succeeded ret = Utils.hexStringToBytes(resultField.getString("hash")); } - } catch (Exception e) { // Malformed result + } catch (Exception e) { + // Malformed result Log.log(Level.WARNING, "Result parsing failed, result of sending was: \n" + result.toString(), e); } return ret; @@ -126,7 +129,8 @@ public JSONObject status() { private JSONObject getError(JSONObject obj) { try { return obj.getJSONObject("error"); - } catch (Exception e) { //could not find the 'error' in JSON, result was OK. + } catch (Exception e) { + //could not find the 'error' in JSON, result was OK. Log.log(Level.FINER, "No error found: ", e); return null; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIServer.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIServer.java index 66db06c..5a04283 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIServer.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIServer.java @@ -40,6 +40,7 @@ public class ABCIServer implements ABCIAPI { private final TendermintChain chain; private final Block genesisBlock; + /** * @param chain - the main chain this server is part of * @param genesisBlock - the genesis (initial) block for the entire system diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java index 4bbe524..65995d9 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChain.java @@ -1,9 +1,7 @@ package nl.tudelft.blockchain.scaleoutdistributedledger.model.mainchain.tendermint; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -35,8 +33,6 @@ public final class TendermintChain implements MainChain { private ExecutorService threadPool; private Set<Sha256Hash> cache; private final Object cacheLock = new Object(); - private Map<Integer, Set<Integer>> extraCache = new HashMap<>(); - private Map<String, Sha256Hash> superExtraCache = new HashMap<>(); @Getter private long currentHeight; @Getter @@ -137,7 +133,8 @@ protected void initialUpdateCache() { * @param height - The height to update to, if -1 check the needed height with Tendermint */ protected void updateCache(long height) { - if (client == null) return; // If in startup + // If in startup + if (client == null) return; this.threadPool.submit(() -> updateCacheBlocking(height)); } @@ -162,24 +159,15 @@ private void updateCacheBlocking(long height) { synchronized (cacheLock) { for (BlockAbstract abs : abstractsAtCurrentHeight) { cache.add(abs.getBlockHash()); - - //TODO Remove extra cache and super extra cache - int blockNum = abs.getBlockNumber(); - int owner = abs.getOwnerNodeId(); - Set<Integer> setOfBlockNums = extraCache.getOrDefault(owner, new HashSet<>()); - setOfBlockNums.add(blockNum); - extraCache.put(owner, setOfBlockNums); - superExtraCache.putIfAbsent(owner + "-" + blockNum, abs.getBlockHash()); } } } if (currentHeight < height) { Log.log(Level.FINE, "Successfully updated the Tendermint cache for node " + this.app.getLocalStore().getOwnNode().getId() + " from height " + currentHeight + " -> " + height + ", number of cached hashes of abstracts on main chain is now " + cache.size()); - Log.log(Level.FINE, "Node " + this.getApp().getLocalStore().getOwnNode().getId() + " current view of cache is " + this.getCurrentCache()); - Log.log(Level.FINE, "Node " + this.getApp().getLocalStore().getOwnNode().getId() + " current view of super cache is " + this.getSuperExtraCache()); } - currentHeight = Math.max(currentHeight, height); // For concurrency reasons use the maximum + // For concurrency reasons use the maximum + currentHeight = Math.max(currentHeight, height); } /** @@ -202,26 +190,16 @@ public Sha256Hash commitAbstract(BlockAbstract abs) { return Sha256Hash.withHash(hash); } } + + @Override + public boolean isPresent(Sha256Hash hash) { + return cache.contains(hash); + } @Override public boolean isPresent(Block block) { Sha256Hash blockHash = block.getHash(); return cache.contains(blockHash); - -// //TODO IMPORTANT Decide what to do here -// if (cache.contains(blockHash)) { -// return true; -// } else { -//// Log.log(Level.INFO, "Node" + this.getApp().getLocalStore().getOwnNode() + " checking cache for " + block.getOwner().getId() + "-" + block.getNumber() + ": " + blockHash + ", the set is " + this.cache); -// // We could miss some blocks in our cache, so update and wait for the results -// //TODO We might not want to update here. The cache should be enough -// updateCacheBlocking(-1, true); -//// Log.log(Level.INFO, "Node" + this.getApp().getLocalStore().getOwnNode() + " did not find " + blockHash + ", so updated the cache and now the set is " + this.cache); -// return cache.contains(blockHash); -// //TODO: We might want to check the actual main chain in the false case -// // For when an abstract is in a block that is not yet closed by an ENDBLOCK -// // This now works because the block size is 1 -// } } @Override @@ -238,17 +216,4 @@ public boolean isInCache(Block block) { boolean addToCache(Sha256Hash genesisBlockHash) { return cache.add(genesisBlockHash); } - - public Map<Integer, Set<Integer>> getCurrentCache() { - synchronized (cacheLock) { - return new HashMap<>(this.extraCache); - } - } - - public Map<String, Sha256Hash> getSuperExtraCache() { - synchronized (cacheLock) { - return new HashMap<>(this.superExtraCache); - } - } - } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java index e28645b..4241a11 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/Simulation.java @@ -1,7 +1,12 @@ package nl.tudelft.blockchain.scaleoutdistributedledger.simulation; -import lombok.Getter; -import lombok.Setter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; import nl.tudelft.blockchain.scaleoutdistributedledger.Application; import nl.tudelft.blockchain.scaleoutdistributedledger.message.Message; @@ -18,16 +23,13 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.sockets.SocketClient; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; -import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Level; +import lombok.Getter; +import lombok.Setter; /** * Class for simulations. */ public class Simulation { - - @Getter @Setter private ITransactionPattern transactionPattern; @@ -157,7 +159,6 @@ public void initialize() { //Have everyone update their nodes list broadcastMessage(new UpdateNodesMessage()); - Log.log(Level.INFO, "[Simulation] Initialized"); state = SimulationState.INITIALIZED; } diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java index edec264..3740138 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/tendermint/TendermintHelper.java @@ -8,11 +8,18 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TreeSet; import java.util.logging.Level; import java.util.stream.Collectors; import java.util.stream.IntStream; +import org.apache.commons.io.FileUtils; import org.json.JSONArray; import org.json.JSONObject; @@ -24,7 +31,6 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.model.Transaction; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Log; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Utils; -import org.apache.commons.io.FileUtils; /** * A class to help with using tendermint. @@ -46,6 +52,7 @@ private TendermintHelper() {} * If the file is already there, it is overridden. * WARNING: providing custom keyPair does not work until jABCI gets updated to TM15 * @param keyPair - the public/private keypair that should be used, null if to be generated + * @param nodeNumber - the number of the node to generate a key for * @return the public/private key pair generated if none provided, the same if provided, null if method failed. */ public static Ed25519Key generatePrivValidatorFile(Ed25519Key keyPair, int nodeNumber) { @@ -65,13 +72,14 @@ public static Ed25519Key generatePrivValidatorFile(Ed25519Key keyPair, int nodeN try { final Process ps = rt.exec(script.toString(), envVariables); ps.waitFor(); - BufferedReader stdInput = new BufferedReader(new InputStreamReader(ps.getInputStream())); - // read the output from the command - String s; - while ((s = stdInput.readLine()) != null) { - generatedJsonString.append(s); + + try (BufferedReader stdInput = new BufferedReader(new InputStreamReader(ps.getInputStream()))) { + // read the output from the command + String s; + while ((s = stdInput.readLine()) != null) { + generatedJsonString.append(s); + } } - stdInput.close(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return null; @@ -246,6 +254,7 @@ public static boolean generateGenesisFiles(Date genesisTime, * --p2p.seeds=[peerAddresses] * @param nodeBasePort - the base port for the node (ie the lowest assigned port for the node, the port+0) * @param peerAddresses - a list of addresses (with ports, which should be basePort+1) of *other* nodes + * @param nodeNumber - the number of the node to run * @throws IOException - if an I/O error occurs */ public static void runTendermintNode(int nodeBasePort, List<String> peerAddresses, int nodeNumber) throws IOException { diff --git a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java index 34bc172..9bf9031 100644 --- a/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java +++ b/src/main/java/nl/tudelft/blockchain/scaleoutdistributedledger/simulation/transactionpattern/ITransactionPattern.java @@ -68,12 +68,10 @@ public default void doAction(LocalStore localStore) throws InterruptedException Node receiver = selectNode(localStore); Log.log(Level.FINE, "Going to make transaction: $ " + amount + " from " + ownNodeId + " -> " + receiver.getId()); - //TODO IMPORTANT Removed synchronization on own chain. Transaction creator should be safe. //Create the transaction TransactionCreator creator = new TransactionCreator(localStore, receiver, amount); Transaction transaction = creator.createTransaction(); - //TODO how many transactions do we put in one block? //Add block to local chain Block newBlock = ownNode.getChain().appendNewBlock(); newBlock.addTransaction(transaction); @@ -101,8 +99,6 @@ public default boolean shouldCommitBlocks(Block lastBlock, Block lastCommitted) */ public default void commitBlocks(LocalStore localStore, boolean force) throws InterruptedException { Chain ownChain = localStore.getOwnNode().getChain(); - - //TODO IMPORTANT Removed synchronization on own chain Block lastBlock = ownChain.getLastBlock(); Block lastCommitted = ownChain.getLastCommittedBlock(); diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStoreTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStoreTest.java index b9cf1f2..6798f9d 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStoreTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/LocalStoreTest.java @@ -1,14 +1,15 @@ package nl.tudelft.blockchain.scaleoutdistributedledger; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + +import org.junit.Before; +import org.junit.Test; + import nl.tudelft.blockchain.scaleoutdistributedledger.model.Block; -import nl.tudelft.blockchain.scaleoutdistributedledger.model.Node; import nl.tudelft.blockchain.scaleoutdistributedledger.model.OwnNode; import nl.tudelft.blockchain.scaleoutdistributedledger.model.Transaction; import nl.tudelft.blockchain.scaleoutdistributedledger.test.utils.TestHelper; -import static org.junit.Assert.assertEquals; -import org.junit.Before; -import org.junit.Test; -import static org.mockito.Mockito.mock; /** * Class to test {@link LocalStore}. diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/BlockTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/BlockTest.java index 5464864..b9e7eb9 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/BlockTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/BlockTest.java @@ -1,15 +1,16 @@ package nl.tudelft.blockchain.scaleoutdistributedledger.model; -import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Utils; -import java.io.ByteArrayOutputStream; -import java.security.SignatureException; import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; + +import java.security.SignatureException; import java.util.ArrayList; -import nl.tudelft.blockchain.scaleoutdistributedledger.Application; -import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; + import org.junit.Before; import org.junit.Test; -import static org.mockito.Mockito.mock; + +import nl.tudelft.blockchain.scaleoutdistributedledger.Application; +import nl.tudelft.blockchain.scaleoutdistributedledger.LocalStore; /** * Test class for {@link Block}. diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ProofTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ProofTest.java index 4df057b..d38a4ab 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ProofTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/ProofTest.java @@ -26,8 +26,6 @@ public class ProofTest { private Application appMock; private Block genesisBlock; - private Proof proof; - /** * @throws Exception - Will not occur. */ diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java index 924f1f9..52c325c 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/SerializationTest.java @@ -30,8 +30,6 @@ public class SerializationTest { private LocalStore aliceLocalStore; - private LocalStore bobLocalStore; - private LocalStore charlieLocalStore; private Block genesisBlock; @@ -68,7 +66,7 @@ public void setUp() { this.charlieNode = setupNode(2); // Setup LocalStore for all thhree nodes this.aliceLocalStore = setupLocalStore(this.aliceNode); - this.bobLocalStore = setupLocalStore(this.bobNode); + setupLocalStore(this.bobNode); this.charlieLocalStore = setupLocalStore(this.charlieNode); // Send 100 coins from Alice to Bob this.transactionSource = generateTransaction(this.aliceNode, this.bobNode, 100, this.genesisBlock.getTransactions().get(0)); @@ -144,7 +142,7 @@ public Transaction generateTransaction(Node sender, Node receiver, long amount, @Test public void testGensisBlockMessage_Valid() throws IOException { // Encode genesis block - BlockMessage genesisBlockMessage = new BlockMessage(this.genesisBlock, this.aliceNode); + BlockMessage genesisBlockMessage = new BlockMessage(this.genesisBlock); // Check assertEquals(this.genesisBlock.getNumber(), genesisBlockMessage.getNumber()); assertEquals(-1, genesisBlockMessage.getPreviousBlockNumber()); @@ -160,7 +158,7 @@ public void testGensisBlockMessage_Valid() throws IOException { @Test public void testTransactionMessage_Valid() throws IOException { // Encode Transaction into TransactionMessage - TransactionMessage transactionMessage = new TransactionMessage(this.transaction, this.transaction.getReceiver()); + TransactionMessage transactionMessage = new TransactionMessage(this.transaction); // Check assertEquals(this.transaction.getNumber(), transactionMessage.getNumber()); assertEquals(this.transaction.getSender().getId(), transactionMessage.getSenderId()); @@ -194,7 +192,7 @@ public void testProofMessage_Valid() throws IOException { public void testBlockMessage_Valid() throws IOException { // Encode Block into BlockMessage Block aliceBlock = this.aliceNode.getChain().getBlocks().get(1); - BlockMessage blockMessage = new BlockMessage(aliceBlock, this.bobNode); + BlockMessage blockMessage = new BlockMessage(aliceBlock); // Check assertEquals(aliceBlock.getNumber(), blockMessage.getNumber()); assertEquals(aliceBlock.getPreviousBlock().getNumber(), blockMessage.getPreviousBlockNumber()); diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIClientTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIClientTest.java index 15ee2b1..cc1e31d 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIClientTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/ABCIClientTest.java @@ -4,17 +4,15 @@ import nl.tudelft.blockchain.scaleoutdistributedledger.model.Sha256Hash; import nl.tudelft.blockchain.scaleoutdistributedledger.test.utils.SilencedTestClass; import nl.tudelft.blockchain.scaleoutdistributedledger.utils.Utils; + import org.json.JSONArray; import org.json.JSONObject; import org.junit.Before; import org.junit.Test; - -import java.util.Arrays; import java.util.List; import static org.junit.Assert.*; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; /** @@ -38,7 +36,7 @@ public void setUp() { @Test public void testCommitSuccess() { String hash = "AAFF"; - BlockAbstract abs = new BlockAbstract(0,0, null, null); + BlockAbstract abs = new BlockAbstract(0, 0, null, null); JSONObject json = new JSONObject(); json.put("result", json); json.put("deliver_tx", json); @@ -46,7 +44,7 @@ public void testCommitSuccess() { json.put("hash", hash); doReturn(json).when(instance).sendRequest(anyString(), any()); - assertTrue(Arrays.equals(Utils.hexStringToBytes(hash), instance.commit(abs))); + assertArrayEquals(Utils.hexStringToBytes(hash), instance.commit(abs)); } /** @@ -54,8 +52,7 @@ public void testCommitSuccess() { */ @Test public void testCommitError() { - String hash = "AAFF"; - BlockAbstract abs = new BlockAbstract(0,0, null, null); + BlockAbstract abs = new BlockAbstract(0, 0, null, null); JSONObject json = new JSONObject(); JSONObject jsonError = new JSONObject(); json.put("error", jsonError); @@ -70,7 +67,7 @@ public void testCommitError() { */ @Test public void testCommitFail() { - BlockAbstract abs = new BlockAbstract(0,0, null, null); + BlockAbstract abs = new BlockAbstract(0, 0, null, null); doReturn(null).when(instance).sendRequest(anyString(), any()); assertNull(instance.commit(abs)); diff --git a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChainTest.java b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChainTest.java index 4f70696..548ee34 100644 --- a/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChainTest.java +++ b/src/test/java/nl/tudelft/blockchain/scaleoutdistributedledger/model/mainchain/tendermint/TendermintChainTest.java @@ -138,24 +138,7 @@ public void testIsPresentAlreadyInCache() { Sha256Hash hash = Sha256Hash.withHash(Utils.hexStringToBytes("FF11")); cache.add(hash); -// assertTrue(instance.isPresent(hash)); - } - - /** - * Test isPresent when the data is not in the cache initially, but an update fixes that. - */ - @Test - public void testIsPresentInCacheAfterUpdate() { - Sha256Hash hash = Sha256Hash.withHash(Utils.hexStringToBytes("FF11")); - - JSONObject json = new JSONObject(); - json.put("latest_block_height", 1); - when(clientMock.status()).thenReturn(json); - List<BlockAbstract> abss = new ArrayList<>(); - abss.add(new BlockAbstract(0, 0, hash, null)); - when(clientMock.query(anyLong())).thenReturn(abss); - -// assertTrue(instance.isPresent(hash)); + assertTrue(instance.isPresent(hash)); } /** @@ -173,7 +156,7 @@ public void testIsPresentNotInCacheAfterUpdate() { abss.add(new BlockAbstract(0, 0, hash2, null)); when(clientMock.query(anyLong())).thenReturn(abss); -// assertFalse(instance.isPresent(hash1)); + assertFalse(instance.isPresent(hash1)); } /**