diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..495fa35c --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/../../../../../:\blockchain\SimBlock\simblock\.idea/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 00000000..61a9130c --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..c3df5ec6 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..83067447 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/simulator/src/main/java/simblock/block/BlockChain.java b/simulator/src/main/java/simblock/block/BlockChain.java new file mode 100644 index 00000000..27289c37 --- /dev/null +++ b/simulator/src/main/java/simblock/block/BlockChain.java @@ -0,0 +1,104 @@ +package simblock.block; + + +import utility.graph.DAG; + +import java.util.ArrayList; +import java.util.Collections; + +public class BlockChain { + private static final BlockChain instance = new BlockChain(); + private DAG chain; + + + private BlockChain(){ + chain = new DAG(); + } + + + public static BlockChain getInstance(){ + return instance; + } + + public void setGenesisBlock(Block genesisBlock){ + chain.setRoot(genesisBlock); + } + + public void addBlock(Block block) throws Exception { + int currentHeight = chain.getCurrentHeight(); + if(block.getHeight() <= currentHeight){ + if(!doesBlockExist(block)){ + chain.addToDAG(block, block.getHeight()); + } + } + else if(block.getHeight() <= currentHeight + 1){ + chain.addToDAG(block, block.getHeight()); + } + else{ + throw new Exception("Problem in Adding block to chain"); + } + + return; + } + + public void putBlockInMainChain(Block block){ + try{ + int blockHeight = block.getHeight(); + ArrayList blocksOnSameHeight = chain.getNodesByHeight(blockHeight); + + if(blocksOnSameHeight.size() == 1){ + return; + } + else{ + int blockIndex = -1; + + for(int i=0; i < blocksOnSameHeight.size(); i++){ + if(block.getId() == blocksOnSameHeight.get(i).getId()){ + blockIndex = i; + break; + } + } + + if(blockIndex == -1){ + throw new Exception("Cannot find Block for Swapping"); + } + + Collections.swap(blocksOnSameHeight, 0, blockIndex); + chain.setDAGHeight(blockHeight, blocksOnSameHeight); + } + } + catch (Exception ex){ + System.out.println(ex.getMessage()); + } + } + + public int getChainHeight(){ + return chain.getCurrentHeight(); + } + + public int getTotalNumberOfBlocksOnChain(){ + return chain.getTotalNumberOfNodes(); + } + + public boolean doesBlockExist(Block block){ + ArrayList blocksOnSameHeight = chain.getNodesByHeight(block.getHeight()); + + for(Block chainBlock : blocksOnSameHeight){ + if(chainBlock.getId() == block.getId()){ + return true; + } + } + + return false; + } + + public ArrayList getMainChain(){ + ArrayList mainChain = new ArrayList<>(); + + for(int i=0; i < chain.getCurrentHeight(); i++){ + mainChain.add(chain.getNodesByHeight(i).get(0)); + } + + return mainChain; + } +} diff --git a/simulator/src/main/java/simblock/node/HonestNode.java b/simulator/src/main/java/simblock/node/HonestNode.java new file mode 100644 index 00000000..11dc9be7 --- /dev/null +++ b/simulator/src/main/java/simblock/node/HonestNode.java @@ -0,0 +1,19 @@ +package simblock.node; + +public class HonestNode extends Node{ + /** + * Instantiates a new Node. + * + * @param nodeID the node id + * @param numConnection the number of connections a node can have + * @param region the region + * @param miningPower the mining power + * @param routingTableName the routing table name + * @param consensusAlgoName the consensus algorithm name + * @param useCBR whether the node uses compact block relay + * @param isChurnNode whether the node causes churn + */ + public HonestNode(int nodeID, int numConnection, int region, long miningPower, String routingTableName, String consensusAlgoName, boolean useCBR, boolean isChurnNode) { + super(nodeID, numConnection, region, miningPower, routingTableName, consensusAlgoName, useCBR, isChurnNode); + } +} diff --git a/simulator/src/main/java/simblock/node/Node.java b/simulator/src/main/java/simblock/node/Node.java index 0f10375b..7cd332ad 100644 --- a/simulator/src/main/java/simblock/node/Node.java +++ b/simulator/src/main/java/simblock/node/Node.java @@ -22,7 +22,6 @@ import static simblock.settings.SimulationConfiguration.CBR_FAILURE_RATE_FOR_CHURN_NODE; import static simblock.settings.SimulationConfiguration.CBR_FAILURE_RATE_FOR_CONTROL_NODE; import static simblock.settings.SimulationConfiguration.COMPACT_BLOCK_SIZE; -import static simblock.simulator.Main.OUT_JSON_FILE; import static simblock.simulator.Network.getBandwidth; import static simblock.simulator.Simulator.arriveBlock; import static simblock.simulator.Timer.getCurrentTime; @@ -36,6 +35,7 @@ import simblock.block.Block; import simblock.node.consensus.AbstractConsensusAlgo; +import simblock.node.consensus.ProofOfWork; import simblock.node.routing.AbstractRoutingTable; import simblock.task.AbstractMessageTask; import simblock.task.AbstractMintingTask; @@ -52,68 +52,68 @@ public class Node { /** * Unique node ID. */ - private final int nodeID; + protected final int nodeID; /** * Region assigned to the node. */ - private final int region; + protected final int region; /** * Mining power assigned to the node. */ - private final long miningPower; + protected final long miningPower; /** * A nodes routing table. */ - private AbstractRoutingTable routingTable; + protected AbstractRoutingTable routingTable; /** * The consensus algorithm used by the node. */ - private AbstractConsensusAlgo consensusAlgo; + protected AbstractConsensusAlgo consensusAlgo; /** * Whether the node uses compact block relay. */ - private boolean useCBR; + protected boolean useCBR; /** * The node causes churn. */ - private boolean isChurnNode; + protected boolean isChurnNode; /** * The current block. */ - private Block block; + protected Block block; /** * Orphaned blocks known to node. */ - private final Set orphans = new HashSet<>(); + protected final Set orphans = new HashSet<>(); /** * The current minting task */ - private AbstractMintingTask mintingTask = null; + protected AbstractMintingTask mintingTask = null; /** * In the process of sending blocks. */ // TODO verify - private boolean sendingBlock = false; + protected boolean sendingBlock = false; //TODO - private final ArrayList messageQue = new ArrayList<>(); + protected final ArrayList messageQue = new ArrayList<>(); // TODO - private final Set downloadingBlocks = new HashSet<>(); + protected final Set downloadingBlocks = new HashSet<>(); /** * Processing time of tasks expressed in milliseconds. */ - private final long processingTime = 2; + protected final long processingTime = 2; /** * Instantiates a new Node. @@ -140,8 +140,7 @@ public Node( try { this.routingTable = (AbstractRoutingTable) Class.forName(routingTableName).getConstructor( Node.class).newInstance(this); - this.consensusAlgo = (AbstractConsensusAlgo) Class.forName(consensusAlgoName).getConstructor( - Node.class).newInstance(this); + this.consensusAlgo = ProofOfWork.getInstance(); this.setNumConnection(numConnection); } catch (Exception e) { e.printStackTrace(); @@ -273,7 +272,7 @@ public void joinNetwork() { * Mint the genesis block. */ public void genesisBlock() { - Block genesis = this.consensusAlgo.genesisBlock(); + Block genesis = this.consensusAlgo.genesisBlock(this); this.receiveBlock(genesis); } @@ -290,29 +289,16 @@ public void addToChain(Block newBlock) { this.mintingTask = null; } // Update the current block - this.block = newBlock; - printAddBlock(newBlock); + if(newBlock.getHeight() == 0){ + this.block = newBlock; + } + else if(newBlock.getHeight() > this.block.getHeight()){ + this.block = newBlock; + } // Observe and handle new block arrival arriveBlock(newBlock, this); } - /** - * Logs the provided block to the logfile. - * - * @param newBlock the block to be logged - */ - private void printAddBlock(Block newBlock) { - OUT_JSON_FILE.print("{"); - OUT_JSON_FILE.print("\"kind\":\"add-block\","); - OUT_JSON_FILE.print("\"content\":{"); - OUT_JSON_FILE.print("\"timestamp\":" + getCurrentTime() + ","); - OUT_JSON_FILE.print("\"node-id\":" + this.getNodeID() + ","); - OUT_JSON_FILE.print("\"block-id\":" + newBlock.getId()); - OUT_JSON_FILE.print("}"); - OUT_JSON_FILE.print("},"); - OUT_JSON_FILE.flush(); - } - /** * Add orphans. * @@ -321,7 +307,7 @@ private void printAddBlock(Block newBlock) { */ //TODO check this out later public void addOrphans(Block orphanBlock, Block validBlock) { - if (orphanBlock != validBlock) { +/* if (orphanBlock != validBlock) { this.orphans.add(orphanBlock); this.orphans.remove(validBlock); if (validBlock == null || orphanBlock.getHeight() > validBlock.getHeight()) { @@ -331,14 +317,14 @@ public void addOrphans(Block orphanBlock, Block validBlock) { } else { this.addOrphans(orphanBlock, validBlock.getParent()); } - } + }*/ } /** * Generates a new minting task and registers it */ public void minting() { - AbstractMintingTask task = this.consensusAlgo.minting(); + AbstractMintingTask task = this.consensusAlgo.minting(this); this.mintingTask = task; if (task != null) { putTask(task); @@ -363,7 +349,7 @@ public void sendInv(Block block) { * @param block the block */ public void receiveBlock(Block block) { - if (this.consensusAlgo.isReceivedBlockValid(block, this.block)) { + if (this.consensusAlgo.isReceivedBlockValid(this, block, this.block)) { if (this.block != null && !this.block.isOnSameChainAs(block)) { // If orphan mark orphan this.addOrphans(this.block, block); @@ -371,7 +357,7 @@ public void receiveBlock(Block block) { // Else add to canonical chain this.addToChain(block); // Generates a new minting task - this.minting(); + //this.minting(); // Advertise received block this.sendInv(block); } else if (!this.orphans.contains(block) && !block.isOnSameChainAs(this.block)) { @@ -394,7 +380,7 @@ public void receiveMessage(AbstractMessageTask message) { if (message instanceof InvMessageTask) { Block block = ((InvMessageTask) message).getBlock(); if (!this.orphans.contains(block) && !this.downloadingBlocks.contains(block)) { - if (this.consensusAlgo.isReceivedBlockValid(block, this.block)) { + if (this.consensusAlgo.isReceivedBlockValid(this, block, this.block)) { AbstractMessageTask task = new RecMessageTask(this, from, block); putTask(task); downloadingBlocks.add(block); diff --git a/simulator/src/main/java/simblock/node/SelfishNode.java b/simulator/src/main/java/simblock/node/SelfishNode.java new file mode 100644 index 00000000..3a1f277e --- /dev/null +++ b/simulator/src/main/java/simblock/node/SelfishNode.java @@ -0,0 +1,278 @@ +package simblock.node; + +import simblock.block.Block; +import simblock.block.BlockChain; +import simblock.task.*; + +import java.io.PrintWriter; +import java.util.*; + +import static simblock.settings.SimulationConfiguration.*; +import static simblock.simulator.Simulator.arriveBlock; +import static simblock.simulator.Timer.putTask; + +public class SelfishNode extends Node { + private final ArrayList privateChain = new ArrayList(); + private final ArrayList publicChain = new ArrayList(); + private final HashSet seenBlock = new HashSet<>(); + + private boolean attackStarted = false; + + private int selfishMinerWinBlock = 0; + private int honestMinerWinBlock = 0; + private int minedBlock = 0; + + protected SelfishMiningTask mintingTask = null; + + public SelfishNode(int nodeID, int numConnection, int region, long miningPower, String routingTableName, + String consensusAlgoName, boolean useCBR, boolean isChurnNode) { + super(nodeID, numConnection, region, miningPower, routingTableName, consensusAlgoName, useCBR, isChurnNode); + } + + public void selfishMinting() { + SelfishMiningTask task = (SelfishMiningTask) this.consensusAlgo.minting(this); + this.mintingTask = task; + if (task != null) { + putTask(task); + } + } + + public void receiveBlock(Block block) { + boolean seenBlock = addToSeenBlock(block); + try{ + BlockChain.getInstance().addBlock(block); + } + catch (Exception ex){ + System.out.println("Public Exception!"); + } + if (this.consensusAlgo.isReceivedBlockValid(this, block, this.block) && !seenBlock) { + if (this.block != null && !this.block.isOnSameChainAs(block)) { + // If orphan mark orphan + this.addOrphans(this.block, block); + } + + if(block.getHeight() != 0){ + if(attackStarted){ + this.honestMiningDecision(block); + } + else{ + this.honestMinerWinBlock++; + } + } + else{ + BlockChain.getInstance().setGenesisBlock(block); + } + // Else add to canonical chain + this.addToChain(block); + // Advertise received block + this.sendInv(block); + } else if (!this.orphans.contains(block) && !block.isOnSameChainAs(this.block)) { + // TODO better understand - what if orphan is not valid? + // If the block was not valid but was an unknown orphan and is not on the same chain as the + // current block + this.addOrphans(block, this.block); + arriveBlock(block, this); + } + } + + public void receiveSelfishBlock(Block block){ + if (this.consensusAlgo.isReceivedBlockValid(this, block, this.block)) { + if (this.block != null && !this.block.isOnSameChainAs(block)) { + // If orphan mark orphan + this.addOrphans(this.block, block); + } + + attackStarted = true; + + int delta = calculateDelta(); + this.addToPrivateChain(block); +// this.addToSeenBlock(block); + + if(delta == 0 && this.calculateSelfishChainLength() == 2){ + this.selfishMinerWinBlock += 2; + for(Block privateBlock : privateChain){ + try{ + BlockChain.getInstance().addBlock(privateBlock); + BlockChain.getInstance().putBlockInMainChain(privateBlock); + } + catch (Exception ex){ + System.out.println("Private Exception!"); + } + } + this.stopAttack(); + } + + //this.selfishMinting(); + } else if (!this.orphans.contains(block) && !block.isOnSameChainAs(this.block)) { + // TODO better understand - what if orphan is not valid? + // If the block was not valid but was an unknown orphan and is not on the same chain as the + // current block + this.addOrphans(block, this.block); + arriveBlock(block, this); + } + } + + public Block getBlock() { + return privateChain.size() > 0 ? privateChain.get(privateChain.size() - 1) : this.block; + } + + public void printSelfishMiningStatus(){ + if(privateChain.size() > 0){ + //this.selfishMinerWinBlock += this.privateChain.size(); + } + System.out.println("Selfish Win Block : " + this.selfishMinerWinBlock); + System.out.println("Honest win Block : " + this.honestMinerWinBlock); + this.minedBlock = this.selfishMinerWinBlock + this.honestMinerWinBlock; + System.out.println("Mined Block : " + this.minedBlock); + System.out.println("Relative Revenue : " + ((double)selfishMinerWinBlock / this.minedBlock)); + System.out.println("**********************************************************************"); + int a = BlockChain.getInstance().getTotalNumberOfBlocksOnChain(); + ArrayList mainChain = BlockChain.getInstance().getMainChain(); + int newSelfishWin = 0; + int newHonestWin = 0; + for(Block mainBlock : mainChain){ + if(mainBlock.getMinter() instanceof SelfishNode){ + newSelfishWin++; + } + else{ + newHonestWin++; + } + } + System.out.println("New Selfish Win Block : " + newSelfishWin); + System.out.println("New Honest win Block : " + newHonestWin); + int newMinedBlock = newSelfishWin + newHonestWin; + System.out.println("New Mined Block : " + newMinedBlock); + System.out.println("Relative Revenue : " + ((double)newSelfishWin / newMinedBlock)); + System.out.println("New Main Chain Size : " + BlockChain.getInstance().getTotalNumberOfBlocksOnChain()); + } + + public int getSeenBlockSize(){ + return this.seenBlock.size(); + } + + public int getPrivateChainSize(){ + return this.privateChain.size(); + } + + public int getPublicChainSize(){ + return this.publicChain.size(); + } + + private int calculateDelta(){ + return this.privateChain.size() - this.publicChain.size(); + } + + private int calculateSelfishChainLength(){ + return this.privateChain.size(); + } + + private void addToPublicChain(Block block){ + try{ + int forkHeight = privateChain.get(0).getHeight(); + if(block.getHeight() >= forkHeight){ + this.publicChain.add(block); + } + } + catch (Exception ex){ + System.out.println("Exception"); + } + } + + private void addToPrivateChain(Block block){ + if(!isBlockInPrivateChain(block) && !seenBlock(block)){ + this.privateChain.add(block); + } + } + + private boolean isBlockInPrivateChain(Block block){ + for(Block privateBlock : privateChain){ + if(privateBlock.getHeight() == block.getHeight()){ + return true; + } + } + + return false; + } + + private void stopAttack(){ + for(Block privateBlock : privateChain){ + this.addToSeenBlock(privateBlock); + this.addToChain(privateBlock); + this.sendInv(privateBlock); + } + this.privateChain.clear(); + this.publicChain.clear(); + this.attackStarted = false; + } + + private void honestMiningDecision(Block honestBlock){ + int delta = this.calculateDelta(); + this.addToPublicChain(block); + + if(delta == 0 && this.privateChain.size() == 0){ + //!< State 2 + this.honestMinerWinBlock += 1; + BlockChain.getInstance().putBlockInMainChain(honestBlock); + this.stopAttack(); + } + else if(delta == 0 && this.privateChain.size() == 1){ + //!< State 3 + float generatedRandom = (float)Math.random(); + + if(generatedRandom < SELFISH_MINER_GAMMA){ + this.honestMinerWinBlock += 1; + this.selfishMinerWinBlock += 1; + BlockChain.getInstance().putBlockInMainChain(privateChain.get(0)); + BlockChain.getInstance().putBlockInMainChain(honestBlock); + } + else{ + this.honestMinerWinBlock += 2; + } + + this.stopAttack(); + } + else if(delta == 1){ + //!< State 4 + //!< Nothing to do...Another state define result of this state + } + else if(delta == 2){ + //!< State 5 + this.selfishMinerWinBlock += this.privateChain.size(); + for(Block privateBlock : privateChain){ + try{ + BlockChain.getInstance().addBlock(privateBlock); + BlockChain.getInstance().putBlockInMainChain(privateBlock); + } + catch (Exception ex){ + System.out.println("Private 2 Exception!"); + } + } + this.stopAttack(); + } + else if(delta > 2){ + //!< State 6 + //!< Nothing to do...Another state define result of this state + } + } + + private boolean addToSeenBlock(Block block){ + for(int seen : seenBlock){ + if(seen == block.getHeight()){ + return true; + } + } + seenBlock.add(block.getHeight()); + System.out.println(block.getHeight()); + return false; + } + + private boolean seenBlock(Block block){ + for(int seen : seenBlock){ + if(seen == block.getHeight()){ + return true; + } + } + + return false; + } +} diff --git a/simulator/src/main/java/simblock/node/consensus/AbstractConsensusAlgo.java b/simulator/src/main/java/simblock/node/consensus/AbstractConsensusAlgo.java index 86049c61..5bd9f6c5 100644 --- a/simulator/src/main/java/simblock/node/consensus/AbstractConsensusAlgo.java +++ b/simulator/src/main/java/simblock/node/consensus/AbstractConsensusAlgo.java @@ -24,24 +24,8 @@ * The type Abstract consensus algorithm. */ public abstract class AbstractConsensusAlgo { - private final Node selfNode; - /** - * Instantiates a new Abstract consensus algo. - * - * @param selfNode the self node - */ - public AbstractConsensusAlgo(Node selfNode) { - this.selfNode = selfNode; - } - - /** - * Gets the node using this consensus algorithm. - * - * @return the self node - */ - public Node getSelfNode() { - return this.selfNode; + public AbstractConsensusAlgo() { } /** @@ -49,7 +33,7 @@ public Node getSelfNode() { * * @return the abstract minting task */ - public abstract AbstractMintingTask minting(); + public abstract AbstractMintingTask minting(Node selfNode); /** * Tests if the receivedBlock is valid with regards to the current block. @@ -58,12 +42,12 @@ public Node getSelfNode() { * @param currentBlock the current block * @return true if block is valid false otherwise */ - public abstract boolean isReceivedBlockValid(Block receivedBlock, Block currentBlock); + public abstract boolean isReceivedBlockValid(Node selfNode, Block receivedBlock, Block currentBlock); /** * Gets the genesis block. * * @return the genesis block */ - public abstract Block genesisBlock(); + public abstract Block genesisBlock(Node selfNode); } diff --git a/simulator/src/main/java/simblock/node/consensus/ProofOfWork.java b/simulator/src/main/java/simblock/node/consensus/ProofOfWork.java index d038fd85..74ed226c 100644 --- a/simulator/src/main/java/simblock/node/consensus/ProofOfWork.java +++ b/simulator/src/main/java/simblock/node/consensus/ProofOfWork.java @@ -21,35 +21,42 @@ import java.math.BigInteger; import simblock.block.Block; import simblock.block.ProofOfWorkBlock; +import simblock.node.HonestNode; import simblock.node.Node; +import simblock.task.AbstractMintingTask; import simblock.task.MiningTask; +import simblock.task.SelfishMiningTask; /** * The type Proof of work. */ @SuppressWarnings("unused") public class ProofOfWork extends AbstractConsensusAlgo { - /** - * Instantiates a new Proof of work consensus algorithm. - * - * @param selfNode the self node - */ - public ProofOfWork(Node selfNode) { - super(selfNode); + private static final ProofOfWork instance = new ProofOfWork(); + private ProofOfWork(){ + + } + public static ProofOfWork getInstance(){ + return instance; } /** * Mints a new block by simulating Proof of Work. */ @Override - public MiningTask minting() { - Node selfNode = this.getSelfNode(); + public AbstractMintingTask minting(Node selfNode) { ProofOfWorkBlock parent = (ProofOfWorkBlock) selfNode.getBlock(); BigInteger difficulty = parent.getNextDifficulty(); double p = 1.0 / difficulty.doubleValue(); double u = random.nextDouble(); - return p <= Math.pow(2, -53) ? null : new MiningTask(selfNode, (long) (Math.log(u) / Math.log( - 1.0 - p) / selfNode.getMiningPower()), difficulty); + if(selfNode instanceof HonestNode){ + return new MiningTask(selfNode, (long) (Math.log(u) / Math.log( + 1.0 - p) / selfNode.getMiningPower()), difficulty); + } + else{ + return new SelfishMiningTask(selfNode, (long) (Math.log(u) / Math.log( + 1.0 - p) / selfNode.getMiningPower()), difficulty); + } } /** @@ -63,11 +70,11 @@ public MiningTask minting() { * @return true if block is valid false otherwise */ @Override - public boolean isReceivedBlockValid(Block receivedBlock, Block currentBlock) { - if (!(receivedBlock instanceof ProofOfWorkBlock)) { + public boolean isReceivedBlockValid(Node selfNode, Block receivedBlock, Block currentBlock) { +/* if (!(receivedBlock instanceof ProofOfWorkBlock)) { return false; - } - ProofOfWorkBlock recPoWBlock = (ProofOfWorkBlock) receivedBlock; + }*/ +/* ProofOfWorkBlock recPoWBlock = (ProofOfWorkBlock) receivedBlock; ProofOfWorkBlock currPoWBlock = (ProofOfWorkBlock) currentBlock; int receivedBlockHeight = receivedBlock.getHeight(); ProofOfWorkBlock receivedBlockParent = receivedBlockHeight == 0 ? null : @@ -80,12 +87,14 @@ public boolean isReceivedBlockValid(Block receivedBlock, Block currentBlock) { ) && ( currentBlock == null || recPoWBlock.getTotalDifficulty().compareTo(currPoWBlock.getTotalDifficulty()) > 0 - ); + );*/ + + return true; } @Override - public ProofOfWorkBlock genesisBlock() { - return ProofOfWorkBlock.genesisBlock(this.getSelfNode()); + public ProofOfWorkBlock genesisBlock(Node selfNode) { + return ProofOfWorkBlock.genesisBlock(selfNode); } } diff --git a/simulator/src/main/java/simblock/node/consensus/SampleProofOfStake.java b/simulator/src/main/java/simblock/node/consensus/SampleProofOfStake.java index e4409297..87559335 100644 --- a/simulator/src/main/java/simblock/node/consensus/SampleProofOfStake.java +++ b/simulator/src/main/java/simblock/node/consensus/SampleProofOfStake.java @@ -29,18 +29,11 @@ */ @SuppressWarnings("unused") public class SampleProofOfStake extends AbstractConsensusAlgo { - /** - * Instantiates a new Sample proof of stake. - * - * @param selfNode the self node - */ - public SampleProofOfStake(Node selfNode) { - super(selfNode); + public SampleProofOfStake() { } @Override - public SampleStakingTask minting() { - Node selfNode = this.getSelfNode(); + public SampleStakingTask minting(Node selfNode) { SamplePoSBlock parent = (SamplePoSBlock) selfNode.getBlock(); BigInteger difficulty = parent.getNextDifficulty(); double p = parent.getCoinage(selfNode).getCoinage().doubleValue() / difficulty.doubleValue(); @@ -53,7 +46,7 @@ public SampleStakingTask minting() { @SuppressWarnings("CheckStyle") @Override - public boolean isReceivedBlockValid(Block receivedBlock, Block currentBlock) { + public boolean isReceivedBlockValid(Node selfNode, Block receivedBlock, Block currentBlock) { if (!(receivedBlock instanceof SamplePoSBlock)) { return false; } @@ -74,7 +67,7 @@ public boolean isReceivedBlockValid(Block receivedBlock, Block currentBlock) { } @Override - public SamplePoSBlock genesisBlock() { - return SamplePoSBlock.genesisBlock(this.getSelfNode()); + public SamplePoSBlock genesisBlock(Node selfNode) { + return SamplePoSBlock.genesisBlock(selfNode); } } diff --git a/simulator/src/main/java/simblock/node/routing/BitcoinCoreTable.java b/simulator/src/main/java/simblock/node/routing/BitcoinCoreTable.java index 1c2b5107..e12793cc 100644 --- a/simulator/src/main/java/simblock/node/routing/BitcoinCoreTable.java +++ b/simulator/src/main/java/simblock/node/routing/BitcoinCoreTable.java @@ -16,7 +16,6 @@ package simblock.node.routing; -import static simblock.simulator.Main.OUT_JSON_FILE; import static simblock.simulator.Simulator.getSimulatedNodes; import static simblock.simulator.Timer.getCurrentTime; @@ -101,7 +100,6 @@ public boolean addNeighbor(Node node) { node) || this.outbound.size() >= this.getNumConnection()) { return false; } else if (this.outbound.add(node) && node.getRoutingTable().addInbound(getSelfNode())) { - printAddLink(node); return true; } else { return false; @@ -117,7 +115,6 @@ public boolean addNeighbor(Node node) { */ public boolean removeNeighbor(Node node) { if (this.outbound.remove(node) && node.getRoutingTable().removeInbound(getSelfNode())) { - printRemoveLink(node); return true; } return false; @@ -131,7 +128,6 @@ public boolean removeNeighbor(Node node) { */ public boolean addInbound(Node from) { if (this.inbound.add(from)) { - printAddLink(from); return true; } return false; @@ -145,36 +141,9 @@ public boolean addInbound(Node from) { */ public boolean removeInbound(Node from) { if (this.inbound.remove(from)) { - printRemoveLink(from); return true; } return false; } - //TODO add example - private void printAddLink(Node endNode) { - OUT_JSON_FILE.print("{"); - OUT_JSON_FILE.print("\"kind\":\"add-link\","); - OUT_JSON_FILE.print("\"content\":{"); - OUT_JSON_FILE.print("\"timestamp\":" + getCurrentTime() + ","); - OUT_JSON_FILE.print("\"begin-node-id\":" + getSelfNode().getNodeID() + ","); - OUT_JSON_FILE.print("\"end-node-id\":" + endNode.getNodeID()); - OUT_JSON_FILE.print("}"); - OUT_JSON_FILE.print("},"); - OUT_JSON_FILE.flush(); - } - - //TODO add example - private void printRemoveLink(Node endNode) { - OUT_JSON_FILE.print("{"); - OUT_JSON_FILE.print("\"kind\":\"remove-link\","); - OUT_JSON_FILE.print("\"content\":{"); - OUT_JSON_FILE.print("\"timestamp\":" + getCurrentTime() + ","); - OUT_JSON_FILE.print("\"begin-node-id\":" + getSelfNode().getNodeID() + ","); - OUT_JSON_FILE.print("\"end-node-id\":" + endNode.getNodeID()); - OUT_JSON_FILE.print("}"); - OUT_JSON_FILE.print("},"); - OUT_JSON_FILE.flush(); - } - } diff --git a/simulator/src/main/java/simblock/settings/SimulationConfiguration.java b/simulator/src/main/java/simblock/settings/SimulationConfiguration.java index f1d2986d..059537c6 100644 --- a/simulator/src/main/java/simblock/settings/SimulationConfiguration.java +++ b/simulator/src/main/java/simblock/settings/SimulationConfiguration.java @@ -24,7 +24,7 @@ public class SimulationConfiguration { * The number of nodes participating in the blockchain network. */ //TODO revert - public static final int NUM_OF_NODES = 300;//600;//800;//6000; + public static final int NUM_OF_NODES = 2;//600;//800;//6000; // public static final int NUM_OF_NODES = 600;//600;//800;//6000; /** @@ -57,6 +57,10 @@ public class SimulationConfiguration { */ public static final int STDEV_OF_MINING_POWER = 100000; + public static final float SELFISH_MINER_ALPHA = 0.49f; + public static final float SELFISH_MINER_GAMMA = 0.5f; + public static final float TOTAL_MINING_POWER = 8000000; + /** * The constant AVERAGE_COINS. */ @@ -78,7 +82,7 @@ public class SimulationConfiguration { */ //TODO revert //public static final int END_BLOCK_HEIGHT = 100; - public static final int END_BLOCK_HEIGHT = 3; + public static final int Iteration_Number = 10000; /** * Block size. (unit: byte). diff --git a/simulator/src/main/java/simblock/simulator/Main.java b/simulator/src/main/java/simblock/simulator/Main.java index 74471d93..84458272 100644 --- a/simulator/src/main/java/simblock/simulator/Main.java +++ b/simulator/src/main/java/simblock/simulator/Main.java @@ -17,21 +17,11 @@ package simblock.simulator; -import static simblock.settings.SimulationConfiguration.ALGO; -import static simblock.settings.SimulationConfiguration.AVERAGE_MINING_POWER; -import static simblock.settings.SimulationConfiguration.END_BLOCK_HEIGHT; -import static simblock.settings.SimulationConfiguration.INTERVAL; -import static simblock.settings.SimulationConfiguration.NUM_OF_NODES; -import static simblock.settings.SimulationConfiguration.STDEV_OF_MINING_POWER; -import static simblock.settings.SimulationConfiguration.TABLE; -import static simblock.settings.SimulationConfiguration.CBR_USAGE_RATE; -import static simblock.settings.SimulationConfiguration.CHURN_NODE_RATE; +import static simblock.settings.SimulationConfiguration.*; import static simblock.simulator.Network.getDegreeDistribution; import static simblock.simulator.Network.getRegionDistribution; -import static simblock.simulator.Network.printRegion; import static simblock.simulator.Simulator.addNode; import static simblock.simulator.Simulator.getSimulatedNodes; -import static simblock.simulator.Simulator.printAllPropagation; import static simblock.simulator.Simulator.setTargetInterval; import static simblock.simulator.Timer.getCurrentTime; import static simblock.simulator.Timer.getTask; @@ -51,8 +41,11 @@ import java.util.Random; import java.util.Set; import simblock.block.Block; +import simblock.node.HonestNode; import simblock.node.Node; +import simblock.node.SelfishNode; import simblock.task.AbstractMintingTask; +import simblock.task.Task; /** @@ -68,46 +61,8 @@ public class Main { * The initial simulation time. */ public static long simulationTime = 0; - /** - * Path to config file. - */ - public static URI CONF_FILE_URI; - /** - * Output path. - */ - public static URI OUT_FILE_URI; - - static { - try { - CONF_FILE_URI = ClassLoader.getSystemResource("simulator.conf").toURI(); - OUT_FILE_URI = CONF_FILE_URI.resolve(new URI("../output/")); - } catch (URISyntaxException e) { - e.printStackTrace(); - } - } - /** - * The output writer. - */ - //TODO use logger - public static PrintWriter OUT_JSON_FILE; - - /** - * The constant STATIC_JSON_FILE. - */ - //TODO use logger - public static PrintWriter STATIC_JSON_FILE; - - static { - try { - OUT_JSON_FILE = new PrintWriter( - new BufferedWriter(new FileWriter(new File(OUT_FILE_URI.resolve("./output.json"))))); - STATIC_JSON_FILE = new PrintWriter( - new BufferedWriter(new FileWriter(new File(OUT_FILE_URI.resolve("./static.json"))))); - } catch (IOException e) { - e.printStackTrace(); - } - } + public static SelfishNode selfishNode; /** * The entry point. @@ -118,41 +73,11 @@ public static void main(String[] args) { final long start = System.currentTimeMillis(); setTargetInterval(INTERVAL); - //start json format - OUT_JSON_FILE.print("["); - OUT_JSON_FILE.flush(); - - // Log regions - printRegion(); - // Setup network constructNetworkWithAllNodes(NUM_OF_NODES); - // Initial block height, we stop at END_BLOCK_HEIGHT - int currentBlockHeight = 1; - - // Iterate over tasks and handle - while (getTask() != null) { - if (getTask() instanceof AbstractMintingTask) { - AbstractMintingTask task = (AbstractMintingTask) getTask(); - if (task.getParent().getHeight() == currentBlockHeight) { - currentBlockHeight++; - } - if (currentBlockHeight > END_BLOCK_HEIGHT) { - break; - } - // Log every 100 blocks and at the second block - // TODO use constants here - if (currentBlockHeight % 100 == 0 || currentBlockHeight == 2) { - writeGraph(currentBlockHeight); - } - } - // Execute task - runTask(); - } - - // Print propagation information about all blocks - printAllPropagation(); + startSimulation(); + selfishNode.printSelfishMiningStatus(); //TODO logger System.out.println(); @@ -194,51 +119,13 @@ public static void main(String[] args) { //Log all orphans // TODO move to method and use logger - for (Block orphan : orphans) { - System.out.println(orphan + ":" + orphan.getHeight()); - } - System.out.println(averageOrphansSize); - - /* - Log in format: - <fork_information, block height, block ID> - fork_information: One of "OnChain" and "Orphan". "OnChain" denote block is on Main chain. - "Orphan" denote block is an orphan block. - */ - // TODO move to method and use logger - try { - FileWriter fw = new FileWriter(new File(OUT_FILE_URI.resolve("./blockList.txt")), false); - PrintWriter pw = new PrintWriter(new BufferedWriter(fw)); - - for (Block b : blockList) { - if (!orphans.contains(b)) { - pw.println("OnChain : " + b.getHeight() + " : " + b); - } else { - pw.println("Orphan : " + b.getHeight() + " : " + b); - } - } - pw.close(); - - } catch (IOException ex) { - ex.printStackTrace(); - } - - OUT_JSON_FILE.print("{"); - OUT_JSON_FILE.print("\"kind\":\"simulation-end\","); - OUT_JSON_FILE.print("\"content\":{"); - OUT_JSON_FILE.print("\"timestamp\":" + getCurrentTime()); - OUT_JSON_FILE.print("}"); - OUT_JSON_FILE.print("}"); - //end json format - OUT_JSON_FILE.print("]"); - OUT_JSON_FILE.close(); - +// for (Block orphan : orphans) { +// System.out.println(orphan + ":" + orphan.getHeight()); +// } +// System.out.println(averageOrphansSize); long end = System.currentTimeMillis(); simulationTime += end - start; - // Log simulation time in milliseconds - System.out.println(simulationTime); - } @@ -311,10 +198,20 @@ public static ArrayList makeRandomList(float rate){ * * @return the number of hash calculations executed per millisecond. */ - public static int genMiningPower() { +/* public static int genMiningPower() { double r = random.nextGaussian(); return Math.max((int) (r * STDEV_OF_MINING_POWER + AVERAGE_MINING_POWER), 1); + }*/ + + public static int getSelfishMiningPower(){ + return (int)(SELFISH_MINER_ALPHA * TOTAL_MINING_POWER); + } + + public static int getHonestMiningPower(){ + float honestMiningPower = (1 - SELFISH_MINER_ALPHA) * TOTAL_MINING_POWER; + + return (int)(honestMiningPower / (NUM_OF_NODES - 1)); } /** @@ -336,28 +233,25 @@ public static void constructNetworkWithAllNodes(int numNodes) { List useCBRNodes = makeRandomList(CBR_USAGE_RATE); // List of churn nodes. - List churnNodes = makeRandomList(CHURN_NODE_RATE); + List churnNodes = makeRandomList(CHURN_NODE_RATE); - for (int id = 1; id <= numNodes; id++) { + // Selfish Miner --> Node ID 1 + //Others Start From 2 + selfishNode = new SelfishNode( + 1, degreeList.get(0) + 1, regionList.get(0), getSelfishMiningPower(), TABLE, + ALGO, useCBRNodes.get(0), churnNodes.get(0) + ); + addNode(selfishNode); + + for (int id = 2; id <= numNodes; id++) { // Each node gets assigned a region, its degree, mining power, routing table and // consensus algorithm - Node node = new Node( - id, degreeList.get(id - 1) + 1, regionList.get(id - 1), genMiningPower(), TABLE, + Node node = new HonestNode( + id, degreeList.get(id - 1) + 1, regionList.get(id - 1), getHonestMiningPower(), TABLE, ALGO, useCBRNodes.get(id - 1), churnNodes.get(id - 1) ); // Add the node to the list of simulated nodes addNode(node); - - OUT_JSON_FILE.print("{"); - OUT_JSON_FILE.print("\"kind\":\"add-node\","); - OUT_JSON_FILE.print("\"content\":{"); - OUT_JSON_FILE.print("\"timestamp\":0,"); - OUT_JSON_FILE.print("\"node-id\":" + id + ","); - OUT_JSON_FILE.print("\"region-id\":" + regionList.get(id - 1)); - OUT_JSON_FILE.print("}"); - OUT_JSON_FILE.print("},"); - OUT_JSON_FILE.flush(); - } // Link newly generated nodes @@ -369,33 +263,26 @@ public static void constructNetworkWithAllNodes(int numNodes) { getSimulatedNodes().get(0).genesisBlock(); } - /** - * Network information when block height is blockHeight, in format: - * - *

nodeID_1, nodeID_2 - * - *

meaning there is a connection from nodeID_1 to right nodeID_1. - * - * @param blockHeight the index of the graph and the current block height - */ - //TODO use logger - public static void writeGraph(int blockHeight) { - try { - FileWriter fw = new FileWriter( - new File(OUT_FILE_URI.resolve("./graph/" + blockHeight + ".txt")), false); - PrintWriter pw = new PrintWriter(new BufferedWriter(fw)); - - for (int index = 1; index <= getSimulatedNodes().size(); index++) { - Node node = getSimulatedNodes().get(index - 1); - for (int i = 0; i < node.getNeighbors().size(); i++) { - Node neighbor = node.getNeighbors().get(i); - pw.println(node.getNodeID() + " " + neighbor.getNodeID()); + public static void startSimulation(){ + + for(int i=0; i < Iteration_Number; i++){ + // Do message passing task! + Task currentTask = getTask(); + while(currentTask != null){ + if (currentTask instanceof AbstractMintingTask) { + AbstractMintingTask task = (AbstractMintingTask) getTask(); } + runTask(); + currentTask = getTask(); } - pw.close(); - } catch (IOException ex) { - ex.printStackTrace(); + float generatedRandom = (float)Math.random(); + if(generatedRandom < SELFISH_MINER_ALPHA){ + selfishNode.selfishMinting(); + } + else{ + getSimulatedNodes().get(1).minting(); + } } } diff --git a/simulator/src/main/java/simblock/simulator/Network.java b/simulator/src/main/java/simblock/simulator/Network.java index 0d908635..a79f47f4 100644 --- a/simulator/src/main/java/simblock/simulator/Network.java +++ b/simulator/src/main/java/simblock/simulator/Network.java @@ -22,7 +22,6 @@ import static simblock.settings.NetworkConfiguration.REGION_DISTRIBUTION; import static simblock.settings.NetworkConfiguration.REGION_LIST; import static simblock.settings.NetworkConfiguration.UPLOAD_BANDWIDTH; -import static simblock.simulator.Main.STATIC_JSON_FILE; import static simblock.simulator.Main.random; import java.util.List; @@ -91,27 +90,4 @@ public static double[] getDegreeDistribution() { return DEGREE_DISTRIBUTION; } - /** - * Prints the currently active regions to outfile. - */ - //TODO - public static void printRegion() { - STATIC_JSON_FILE.print("{\"region\":["); - - int id = 0; - for (; id < REGION_LIST.size() - 1; id++) { - STATIC_JSON_FILE.print("{"); - STATIC_JSON_FILE.print("\"id\":" + id + ","); - STATIC_JSON_FILE.print("\"name\":\"" + REGION_LIST.get(id) + "\""); - STATIC_JSON_FILE.print("},"); - } - - STATIC_JSON_FILE.print("{"); - STATIC_JSON_FILE.print("\"id\":" + id + ","); - STATIC_JSON_FILE.print("\"name\":\"" + REGION_LIST.get(id) + "\""); - STATIC_JSON_FILE.print("}"); - STATIC_JSON_FILE.print("]}"); - STATIC_JSON_FILE.flush(); - STATIC_JSON_FILE.close(); - } } diff --git a/simulator/src/main/java/simblock/simulator/Simulator.java b/simulator/src/main/java/simblock/simulator/Simulator.java index 1ab2016d..8ef5a470 100644 --- a/simulator/src/main/java/simblock/simulator/Simulator.java +++ b/simulator/src/main/java/simblock/simulator/Simulator.java @@ -18,9 +18,8 @@ import static simblock.simulator.Timer.getCurrentTime; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.*; + import simblock.block.Block; import simblock.node.Node; @@ -107,6 +106,7 @@ public static void addNodeWithConnection(Node node) { * A list of observed {@link Block} instances. */ private static final ArrayList observedBlocks = new ArrayList<>(); + private static final Set mainChain = new LinkedHashSet<>(); /** * A list of observed block propagation times. The map key represents the id of the node that @@ -140,7 +140,7 @@ public static void arriveBlock(Block block, Node node) { //TODO move magic number to constant if (observedBlocks.size() > 10) { // After the observed blocks limit is reached, log and remove old blocks by FIFO principle - printPropagation(observedBlocks.get(0), observedPropagations.get(0)); + //printPropagation(observedBlocks.get(0), observedPropagations.get(0)); observedBlocks.remove(0); observedPropagations.remove(0); } @@ -149,6 +149,7 @@ public static void arriveBlock(Block block, Node node) { propagation.put(node.getNodeID(), getCurrentTime() - block.getTime()); // Record the block as seen observedBlocks.add(block); + mainChain.add(block.getHeight()); // Record the propagation time observedPropagations.add(propagation); } @@ -186,4 +187,8 @@ public static void printAllPropagation() { printPropagation(observedBlocks.get(i), observedPropagations.get(i)); } } + + public static int getMainChainSize(){ + return mainChain.size(); + } } diff --git a/simulator/src/main/java/simblock/task/BlockMessageTask.java b/simulator/src/main/java/simblock/task/BlockMessageTask.java index 964691b6..ff586bc0 100644 --- a/simulator/src/main/java/simblock/task/BlockMessageTask.java +++ b/simulator/src/main/java/simblock/task/BlockMessageTask.java @@ -16,7 +16,6 @@ package simblock.task; -import static simblock.simulator.Main.OUT_JSON_FILE; import static simblock.simulator.Network.getLatency; import static simblock.simulator.Timer.getCurrentTime; @@ -65,19 +64,6 @@ public long getInterval() { public void run() { this.getFrom().sendNextBlockMessage(); - - OUT_JSON_FILE.print("{"); - OUT_JSON_FILE.print("\"kind\":\"flow-block\","); - OUT_JSON_FILE.print("\"content\":{"); - OUT_JSON_FILE.print("\"transmission-timestamp\":" + (getCurrentTime() - this.interval) + ","); - OUT_JSON_FILE.print("\"reception-timestamp\":" + getCurrentTime() + ","); - OUT_JSON_FILE.print("\"begin-node-id\":" + getFrom().getNodeID() + ","); - OUT_JSON_FILE.print("\"end-node-id\":" + getTo().getNodeID() + ","); - OUT_JSON_FILE.print("\"block-id\":" + block.getId()); - OUT_JSON_FILE.print("}"); - OUT_JSON_FILE.print("},"); - OUT_JSON_FILE.flush(); - super.run(); } diff --git a/simulator/src/main/java/simblock/task/CmpctBlockMessageTask.java b/simulator/src/main/java/simblock/task/CmpctBlockMessageTask.java index fb08b62b..c31f0f7d 100644 --- a/simulator/src/main/java/simblock/task/CmpctBlockMessageTask.java +++ b/simulator/src/main/java/simblock/task/CmpctBlockMessageTask.java @@ -16,7 +16,6 @@ package simblock.task; -import static simblock.simulator.Main.OUT_JSON_FILE; import static simblock.simulator.Network.getLatency; import static simblock.simulator.Timer.getCurrentTime; @@ -65,19 +64,6 @@ public long getInterval() { public void run() { this.getFrom().sendNextBlockMessage(); - - OUT_JSON_FILE.print("{"); - OUT_JSON_FILE.print("\"kind\":\"flow-block\","); - OUT_JSON_FILE.print("\"content\":{"); - OUT_JSON_FILE.print("\"transmission-timestamp\":" + (getCurrentTime() - this.interval) + ","); - OUT_JSON_FILE.print("\"reception-timestamp\":" + getCurrentTime() + ","); - OUT_JSON_FILE.print("\"begin-node-id\":" + getFrom().getNodeID() + ","); - OUT_JSON_FILE.print("\"end-node-id\":" + getTo().getNodeID() + ","); - OUT_JSON_FILE.print("\"block-id\":" + block.getId()); - OUT_JSON_FILE.print("}"); - OUT_JSON_FILE.print("},"); - OUT_JSON_FILE.flush(); - super.run(); } diff --git a/simulator/src/main/java/simblock/task/SelfishMiningTask.java b/simulator/src/main/java/simblock/task/SelfishMiningTask.java new file mode 100644 index 00000000..628e16dc --- /dev/null +++ b/simulator/src/main/java/simblock/task/SelfishMiningTask.java @@ -0,0 +1,32 @@ +package simblock.task; + +import simblock.block.ProofOfWorkBlock; +import simblock.node.Node; +import simblock.node.SelfishNode; + +import java.math.BigInteger; + +import static simblock.simulator.Timer.getCurrentTime; + +public class SelfishMiningTask extends AbstractMintingTask { + private final BigInteger difficulty; + /** + * Instantiates a new Abstract minting task. + * + * @param minter the minter + * @param interval the interval in milliseconds + */ + public SelfishMiningTask(Node minter, long interval, BigInteger difficulty) { + super(minter, interval); + this.difficulty = difficulty; + } + + @Override + public void run() { + ProofOfWorkBlock createdBlock = new ProofOfWorkBlock( + (ProofOfWorkBlock) this.getParent(), this.getMinter(), getCurrentTime(), + this.difficulty + ); + ((SelfishNode)this.getMinter()).receiveSelfishBlock(createdBlock); + } +} diff --git a/simulator/src/main/java/utility/graph/DAG.java b/simulator/src/main/java/utility/graph/DAG.java new file mode 100644 index 00000000..f6c4745a --- /dev/null +++ b/simulator/src/main/java/utility/graph/DAG.java @@ -0,0 +1,85 @@ +package utility.graph; + + + +import simblock.block.Block; + +import java.util.ArrayList; + +public class DAG { + private ArrayList>> graph; + private int height = -1; + + public DAG(){ + graph = new ArrayList>>(); + } + + public void setRoot(T root){ + DAGNode node = new DAGNode(root); + height = 0; + + graph.add(new ArrayList>()); + graph.get(height).add(node); + + return; + } + + public void addToDAG(T newValue, int newHeight){ + DAGNode node = new DAGNode(newValue); + + if(height >= newHeight){ + graph.get(newHeight).add(node); + } + else{ + graph.add(new ArrayList>()); + height++; + graph.get(newHeight).add(node); + } + + return; + } + + public ArrayList>> getDAG(){ + return graph; + } + + public int getCurrentHeight(){ + return height; + } + + public ArrayList getNodesByHeight(int height){ + ArrayList result = new ArrayList(); + + for(DAGNode node : graph.get(height)){ + result.add(node.getData()); + } + + return result; + } + + public T getNodeByHeightRow(int height, int row){ + return graph.get(height).get(row).getData(); + } + + public int getTotalNumberOfNodes(){ + int counter = 0; + + for(int i=0; i < graph.size(); i++){ + for(int j=0; j < graph.get(i).size(); j++){ + counter++; + } + } + + return counter; + } + + public void setDAGHeight(int height, ArrayList inputNodes){ + ArrayList> result = new ArrayList<>(); + + for(T node : inputNodes){ + result.add(new DAGNode(node)); + } + + graph.set(height, result); + } +} diff --git a/simulator/src/main/java/utility/graph/DAGNode.java b/simulator/src/main/java/utility/graph/DAGNode.java new file mode 100644 index 00000000..999ea02c --- /dev/null +++ b/simulator/src/main/java/utility/graph/DAGNode.java @@ -0,0 +1,13 @@ +package utility.graph; + +public class DAGNode { + private T data; + + public DAGNode(T data){ + this.data = data; + } + + public T getData(){ + return this.data; + } +}