Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main'
Browse files Browse the repository at this point in the history
Former-commit-id: 9ab47fd365beadb06c96f34efd351620d62278a7 [formerly 724ea098c74b5efca28918a86f77b5afc193ac41]
Former-commit-id: b84dc545b0c7a6ee0532c841f416d72639963b36
  • Loading branch information
hyajam committed Sep 1, 2023
2 parents 2b7dfca + 10b538b commit f2f679e
Show file tree
Hide file tree
Showing 31 changed files with 771 additions and 13 deletions.
Binary file added .DS_Store
Binary file not shown.
Binary file not shown.

This file was deleted.

13 changes: 8 additions & 5 deletions src/main/java/jabs/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@

import jabs.ledgerdata.bitcoin.BitcoinBlockWithoutTx;
import jabs.log.*;
import jabs.scenario.AbstractScenario;
import jabs.scenario.BitcoinGlobalNetworkScenario;
import jabs.scenario.NormalEthereumNetworkScenario;
import jabs.scenario.PBFTLANScenario;
import jabs.scenario.*;

import java.io.IOException;
import java.nio.file.Paths;
Expand All @@ -20,7 +17,7 @@ public class Main {
*/
public static void main(String[] args) throws IOException {
AbstractScenario scenario;

/*
// Simulate one day in the life of Bitcoin network
// Nakamoto protocol with block every 600 seconds
// Around 8000 nodes with 30 miners
Expand Down Expand Up @@ -53,5 +50,11 @@ public static void main(String[] args) throws IOException {
40, 3600);
scenario.AddNewLogger(new PBFTCSVLogger(Paths.get("output/pbft-simulation-log.csv")));
scenario.run();
*/
// Simulate Snow LAN network of 40 nodes for 1 hour
scenario = new SnowLANScenario("One hour of a Snow lan Network", 1, 40,
3600);
scenario.AddNewLogger(new SnowCSVLogger(Paths.get("output/snow-simulation-log.csv")));
scenario.run();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package jabs.consensus.algorithm;

import jabs.ledgerdata.Block;
import jabs.ledgerdata.Tx;
import jabs.ledgerdata.Query;

public interface QueryingBasedConsensus <B extends Block<B>, T extends Tx<T>> extends ConsensusAlgorithm<B, T>{
void newIncomingQuery(Query query);
}
302 changes: 302 additions & 0 deletions src/main/java/jabs/consensus/algorithm/Snow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
package jabs.consensus.algorithm;

import jabs.consensus.blockchain.LocalBlockTree;
import jabs.ledgerdata.BlockFactory;
import jabs.ledgerdata.SingleParentBlock;
import jabs.ledgerdata.Tx;
import jabs.ledgerdata.Query;
import jabs.ledgerdata.snow.*;
import jabs.network.message.QueryMessage;
import jabs.network.node.nodes.Node;
import jabs.network.node.nodes.snow.SnowNode;
import jabs.simulator.randengine.RandomnessEngine;

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

/**
* File: Snow.java
* Description: Implements SnowBall protocol for JABS blockchain simulator.
* Author: Siamak Abdi
* Date: August 22, 2023
*/

public class Snow<B extends SingleParentBlock<B>, T extends Tx<T>> extends AbstractChainBasedConsensus<B, T>
implements QueryingBasedConsensus<B, T>, DeterministicFinalityConsensus<B, T> {

static {
try {
File file = new File("output/snow-events-log.txt");
if (file.exists()) {
file.delete();
}
writer = new PrintWriter(new FileWriter(file, true));
} catch (Exception e) {
e.printStackTrace();
}
}
//*-------------------------------------------------------------------------------------------------------
// settings for the Snow protocols
public static int k = 2; // sample size
private int alpha = k/2; // quorum size or threshold
private int beta = 5; // conviction threshold
private double mean = 0.5; // Mean of the normal distribution
private double stdDev = 0.1; // Standard deviation of the normal distribution
//*-------------------------------------------------------------------------------------------------------
private static PrintWriter writer;
private final int numAllParticipants;
private static int numReadyNodes;
private static boolean readyTransfer;
private final HashMap<B, HashMap<Node, Query>> commitQueries = new HashMap<>();
private final static HashSet<Node> committedNodes = new HashSet<>();
public static int numConsensus;
private int numReceivedSamples;
private int conviction;
public boolean consensus;
private SnowPhase snowPhase = SnowPhase.PREPARING;
private SnowMode snowMode = SnowMode.NORMAL;
@Override
public boolean isBlockFinalized(B block) {
return false;
}

@Override
public boolean isTxFinalized(T tx) {
return false;
}

@Override
public int getNumOfFinalizedBlocks() {
return 0;
}

@Override
public int getNumOfFinalizedTxs() {
return 0;
}

public enum SnowPhase {
PREPARING,
COMMITTING
}
private enum SnowMode{
NORMAL,
TRANSFERRING
}

public Snow(LocalBlockTree<B> localBlockTree, int numAllParticipants) {
super(localBlockTree);
this.numAllParticipants = numAllParticipants;
this.currentMainChainHead = localBlockTree.getGenesisBlock();
}

public void newIncomingQuery(Query query) {
if (query instanceof SnowBlockQuery) {
SnowBlockQuery<B> blockQuery = (SnowBlockQuery<B>) query;
B block = blockQuery.getBlock();
SnowNode peer = (SnowNode) this.peerBlockchainNode;
if(peer.currentBlock.getHeight() < block.getHeight()){ // If the node has not yet voted for the new block
double sample = generateNormalSample(this.peerBlockchainNode.getNetwork().getRandom(), mean, stdDev); // decides whether to vote for the new block or not
peer.currentBlock = sample >= 0.5 ? (SnowBlock) block : peer.currentBlock;
}
SnowNode inquirer = (SnowNode) blockQuery.getInquirer();
switch (blockQuery.getQueryType()) {
case PREPARE :
if (!this.localBlockTree.contains(block)) {
this.localBlockTree.add(block);
}
if (this.localBlockTree.getLocalBlock(block).isConnectedToGenesis) {
this.snowPhase = SnowPhase.COMMITTING;
this.peerBlockchainNode.respondQuery(
new QueryMessage(
new SnowCommitQuery<>(this.peerBlockchainNode, peer.currentBlock)
), inquirer
);
}
break;
case COMMIT:
checkQueries(blockQuery, (B) peer.currentBlock, this.commitQueries, SnowPhase.PREPARING);
break;
}
}
}

private void checkQueries(SnowBlockQuery<B> query, B block, HashMap<B, HashMap<Node, Query>> queries, Snow.SnowPhase nextStep) {
this.snowPhase = nextStep;
this.numReceivedSamples++;
List<Node> sampledNeighbors = sampleNeighbors(this.peerBlockchainNode.getNetwork().getRandom(), k);
if (!queries.containsKey(block)) { // the first query reply received for the block
queries.put(block, new HashMap<>());
}
queries.get(block).put(query.getInquirer(), query);
if (numReceivedSamples == k) { // if the node has received all the replies
this.numReceivedSamples = 0;
if(this.snowMode == SnowMode.TRANSFERRING){
if(!readyTransfer){
this.snowMode = SnowMode.NORMAL;
}
queries.clear();
continueSampling(nextStep, sampledNeighbors, block); // Just to participate in the process
}else if(this.snowMode == SnowMode.NORMAL){
if(committedNodes.contains(this.peerBlockchainNode)){ // if the node has reached consensus (beta threshold)
queries.clear();
if(!this.consensus){
checkConsensus(block);
}
if(!readyTransfer){
continueSampling(nextStep, sampledNeighbors, block); // Just to participate in the process
}else {
startGenerateNewBlock(nextStep, sampledNeighbors, block);
}
} else if ((queries.get(block).size() > alpha) && (block.getHeight()>this.currentMainChainHead.getHeight())) { // If a successful query for the block reaches the alpha value
this.conviction++;
queries.clear();
if((this.conviction==beta)){ // if the node's conviction value reaches the beta value (local consensus)
committedNodes.add(this.peerBlockchainNode);
this.conviction = 0;
if(!this.consensus){
checkConsensus(block);
}
}
continueSampling(nextStep, sampledNeighbors, block); // Just to participate in the process
} else {
queries.clear();
this.conviction = 0;
continueSampling(nextStep, sampledNeighbors, block); // Just to participate in the process
}
}
}
}

private void startGenerateNewBlock(Snow.SnowPhase nextStep, List<Node> sampledNeighbors, B block) {
//System.out.println("node "+this.peerBlockchainNode.nodeID+" started"+ " at "+this.peerBlockchainNode.getSimulator().getSimulationTime());
this.conviction = 0;
this.snowMode = SnowMode.TRANSFERRING;
committedNodes.remove(this.peerBlockchainNode);
this.consensus = false;
if(committedNodes.size()==0){
numReadyNodes = 0;
readyTransfer = false;
//System.out.println("All transferred!");
}
switch (nextStep) {
case PREPARING:
if(this.peerBlockchainNode.nodeID == this.getCurrentPrimaryNumber()) { // The next node to start proposing a new block
SnowNode snowNode = (SnowNode) this.peerBlockchainNode;
SnowBlock newBlock = BlockFactory.sampleSnowBlock(this.peerBlockchainNode.getSimulator(), this.peerBlockchainNode.getNetwork().getRandom(),
snowNode, (SnowBlock) block);
snowNode.currentBlock = newBlock;
for (Node destination : sampledNeighbors) {
snowNode.query(
new QueryMessage(
new SnowPrepareQuery<>(snowNode, newBlock)
), destination
);
}
}else {
SnowNode snowNode = (SnowNode) this.peerBlockchainNode;
snowNode.currentBlock = (SnowBlock) block;
for(Node destination:sampledNeighbors){
snowNode.query(
new QueryMessage(
new SnowPrepareQuery<>(snowNode, block)
), destination
);
}
}
break;
}
}

private void continueSampling(Snow.SnowPhase nextStep, List<Node> sampledNeighbors, B block) {
switch (nextStep) {
case PREPARING:
for(Node destination:sampledNeighbors){
this.peerBlockchainNode.query(
new QueryMessage(
new SnowPrepareQuery<>(this.peerBlockchainNode, block)
), destination
);
}
break;
}
}

private void checkConsensus(B block) {
if(committedNodes.size() == getNumAllParticipants()){
//System.out.println("node "+this.peerBlockchainNode.nodeID+" is ready"+ " at "+this.peerBlockchainNode.getSimulator().getSimulationTime());
this.consensus=true;
numReadyNodes++;
if(numReadyNodes==getNumAllParticipants()){
readyTransfer = true;
}
this.currentMainChainHead = block; // accepts the block
updateChain();
writer.println("Consensus occurred in node " + this.peerBlockchainNode.getNodeID() + " for the block " + block.getHeight() + " at " + this.peerBlockchainNode.getSimulator().getSimulationTime());
writer.flush();
}
}

private List<Node> sampleNeighbors(RandomnessEngine randomnessEngine, int k) {
List<Node> neighbors = new ArrayList<>();
neighbors.addAll(this.peerBlockchainNode.getP2pConnections().getNeighbors());
neighbors.remove(this.peerBlockchainNode);
List<Node> sampledNodes = new ArrayList<>();
for (int i = 0; i < k; i++) {
int randomIndex = randomnessEngine.sampleInt(neighbors.size());
sampledNodes.add(neighbors.get(randomIndex));
neighbors.remove(randomIndex);
}
return sampledNodes;
}
private static double generateNormalSample(RandomnessEngine random, double mean, double stdDev) {
return mean + stdDev * random.nextGaussian();
}

@Override
public void newIncomingBlock(B block) {

}

/**
* @param block
* @return
*/
@Override
public boolean isBlockConfirmed(B block) {
return false;
}

/**
* @param block
* @return
*/
@Override
public boolean isBlockValid(B block) {
return false;
}

public int getNumAllParticipants() {
return this.numAllParticipants;
}

public Snow.SnowPhase getSnowPhase() {
return this.snowPhase;
}

public int getCurrentPrimaryNumber() {
return (this.currentMainChainHead.getHeight() % this.numAllParticipants);
}

@Override
protected void updateChain() {
this.confirmedBlocks.add(this.currentMainChainHead);
numConsensus = this.confirmedBlocks.size();
}

}
4 changes: 4 additions & 0 deletions src/main/java/jabs/consensus/config/SnowConsensusConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package jabs.consensus.config;

public class SnowConsensusConfig implements ConsensusAlgorithmConfig{
}
7 changes: 7 additions & 0 deletions src/main/java/jabs/ledgerdata/BlockFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import jabs.network.node.nodes.pbft.PBFTNode;
import jabs.simulator.randengine.RandomnessEngine;
import jabs.simulator.Simulator;
import jabs.ledgerdata.snow.SnowBlock;
import jabs.network.node.nodes.snow.SnowNode;

import java.util.Set;

Expand Down Expand Up @@ -57,6 +59,11 @@ public static PBFTBlock samplePBFTBlock(Simulator simulator, RandomnessEngine ra
simulator.getSimulationTime(), creator, parent); // TODO: Size of PBFT Blocks
}

public static SnowBlock sampleSnowBlock(Simulator simulator, RandomnessEngine randomnessEngine, SnowNode creator, SnowBlock parent) {
return new SnowBlock(sampleBitcoinBlockSize(randomnessEngine), parent.getHeight() + 1,
simulator.getSimulationTime(), creator, parent); // TODO: Size of Snow Blocks
}

public static EthereumBlock sampleEthereumBlock(Simulator simulator, RandomnessEngine randomnessEngine, EthereumMinerNode creator, EthereumBlock parent,
Set<EthereumBlock> uncles, double weight) {
return new EthereumBlock(sampleBitcoinBlockSize(randomnessEngine), parent.getHeight() + 1,
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/jabs/ledgerdata/Query.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package jabs.ledgerdata;

import jabs.network.node.nodes.Node;

public class Query extends BasicData {
private final Node inquirer;

protected Query(int size, Node inquirer) {
super(size);
this.inquirer = inquirer;
}

public Node getInquirer() {
return inquirer;
}

}
Loading

0 comments on commit f2f679e

Please sign in to comment.