Commit d366f99d authored by Abhishek Kumar's avatar Abhishek Kumar

Completed txns code

parent fb9756fb
from utils import * from utils import *
from simulate import Event from simulate import Event
class Block: class Block:
def __init__(self, peer, ID, transactions, previous_block_ID): def __init__(self, nodeID, ID, transactions, previous_block_ID):
# Initialize block attributes # Initialize block attributes
self.blk_Id = ID self.blk_Id = ID
self.transactions = transactions self.transactions = transactions
self.prev_blk_Id = previous_block_ID self.prev_blk_Id = previous_block_ID
self.pow_user_id = peer self.pow_user_id = nodeID
return return
class Blockchain: class Blockchain:
def __init__(self): def __init__(self):
# Initialize blockchain attributes # Initialize blockchain attributes
# TODO: Should broadcast the genesis block
self.chain = {} self.chain = {}
self.genensis_block = Block(0, [], -1) self.genensis_block = Block(-1, gen_hash(GENESIS_SECRET), [], -1)
self.chain[self.genensis_block.blk_Id] = self.genensis_block self.chain[self.genensis_block.blk_Id] = (self.genensis_block, 0) #
self.head = 0 self.head = self.genensis_block.blk_Id
self.len_longest_chain = 1 self.len_longest_chain = 1
self.seen_transactions = set() self.seen_transactions = set()
self.pending_transactions = set() self.pending_transactions = set()
self.next_pow_completion_time = 0 self.next_pow_completion_time = 0
pass
def get_chain_length(self, blk_Id = None): def get_chain_length(self, blk_Id=None):
# Return the length of the longest chain # Return the length of the longest chain
if blk_Id is None: if blk_Id is None:
curr = self.len_longest_chain curr = self.head
curr = blk_Id curr = blk_Id
cnt = 0 cnt = 0
while(curr != -1): while curr != self.genensis_block.blk_Id:
if curr not in self.chain: if curr not in self.chain:
print("ERROR: Block not found in the chain") print("ERROR: Block not found in the chain")
break break
...@@ -63,4 +64,19 @@ class Blockchain: ...@@ -63,4 +64,19 @@ class Blockchain:
""" """
return return
def def print_chain(self):
pass
def already_exist(self, blk):
print(blk, self.chain.get(blk.blk_Id))
exit()
if blk.blk_Id in self.chain:
return True
return False
def received_txn(self, txn):
if txn in self.pending_transactions or txn in self.seen_transactions:
return False
self.pending_transactions.add(txn)
return True
...@@ -3,23 +3,39 @@ from simulate import EventSimulator ...@@ -3,23 +3,39 @@ from simulate import EventSimulator
import random import random
from network import Network from network import Network
def parse_cmd(): def parse_cmd():
""" """
Parse command line arguments Parse command line arguments
""" """
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('-n', '--num_nodes', type = int, default = 50, help="Number of nodes in the network") parser.add_argument(
parser.add_argument('--z0', type = int, default = 10, help="percentage of node with slow bandwidth") "-n", "--num_nodes", type=int, default=50, help="Number of nodes in the network"
parser.add_argument('--z1', type = int, default = 10, help="percentage of node with low CPU") )
parser.add_argument('--T_tx', type = int, default = 10, help="Interarrival time between transactions") parser.add_argument(
parser.add_argument('--I', type = int, default = 100, help="Interarrival time between blocks") "--z0", type=int, default=10, help="percentage of node with slow bandwidth"
parser.add_argument('--T_dij', type = int, default = 10, help="Mean of queuing delay") )
#parser.add_argument('--pij', type = int, default = 10, help="speed of light propagation delay") parser.add_argument(
parser.add_argument('--sim_time', type = int, default = 100, help="Simulation time in seconds") "--z1", type=int, default=10, help="percentage of node with low CPU"
parser.add_argument('--seed', type = int, default = 0, help="Seed for random number generator") )
parser.add_argument(
"--T_tx", type=int, default=10, help="Interarrival time between transactions"
)
parser.add_argument(
"--I", type=int, default=100, help="Interarrival time between blocks"
)
parser.add_argument("--T_dij", type=int, default=10, help="Mean of queuing delay")
# parser.add_argument('--pij', type = int, default = 10, help="speed of light propagation delay")
parser.add_argument(
"--sim_time", type=int, default=100, help="Simulation time in seconds"
)
parser.add_argument(
"--seed", type=int, default=0, help="Seed for random number generator"
)
return parser.parse_args() return parser.parse_args()
if __name__ == '__main__':
if __name__ == "__main__":
args = parse_cmd() args = parse_cmd()
random.seed(args.seed) random.seed(args.seed)
......
...@@ -3,15 +3,22 @@ import random ...@@ -3,15 +3,22 @@ import random
from simulate import Event from simulate import Event
from utils import sample_exponential from utils import sample_exponential
class Network: class Network:
def __init__(self, sim, args): def __init__(self, sim, args):
self.args = args self.args = args
self.sim = sim self.sim = sim
self.pij = float(random.randrange(10, 500)) * 0.001# speed of light propagation delay in ms # speed of light propagation delay in ms
self.pij = float(random.randrange(10, 500)) * 0.001
self.I = 600 self.I = 600
self.create_nodes() self.create_nodes()
self.create_connections() self.create_connections()
self.sim.push_event(Event(0, self.init_simulation,)) self.sim.push_event(
Event(
0,
self.init_simulation,
)
)
return return
def init_simulation(self, args): def init_simulation(self, args):
...@@ -28,13 +35,13 @@ class Network: ...@@ -28,13 +35,13 @@ class Network:
# Create a list of badwidth and cpu type based on z0 and z1 # Create a list of badwidth and cpu type based on z0 and z1
# 0 is slow and 1 is fast # 0 is slow and 1 is fast
bandwidth_type = [1] * self.args.num_nodes bandwidth_type = [1] * self.args.num_nodes
num_slow_bandwidth = int((self.args.z0*self.args.num_nodes)/100) num_slow_bandwidth = int((self.args.z0 * self.args.num_nodes) / 100)
rand_idx = random.sample(range(self.args.num_nodes), num_slow_bandwidth) rand_idx = random.sample(range(self.args.num_nodes), num_slow_bandwidth)
for idx in rand_idx: for idx in rand_idx:
bandwidth_type[idx] = 0 bandwidth_type[idx] = 0
cpu_type = [1] * self.args.num_nodes cpu_type = [1] * self.args.num_nodes
num_slow_cpu = int((self.args.z1*self.args.num_nodes)/100) num_slow_cpu = int((self.args.z1 * self.args.num_nodes) / 100)
rand_idx = random.sample(range(self.args.num_nodes), num_slow_cpu) rand_idx = random.sample(range(self.args.num_nodes), num_slow_cpu)
for idx in rand_idx: for idx in rand_idx:
cpu_type[idx] = 0 cpu_type[idx] = 0
...@@ -43,7 +50,7 @@ class Network: ...@@ -43,7 +50,7 @@ class Network:
# 10 * h_k * (n - num_slow) + h_k * num_slow = 1 # 10 * h_k * (n - num_slow) + h_k * num_slow = 1
# => h_k = 1 / (10 * n - 9 * num_slow) # => h_k = 1 / (10 * n - 9 * num_slow)
h_k_slow = 1 / float(10*self.args.num_nodes - 9*num_slow_cpu) h_k_slow = 1 / float(10 * self.args.num_nodes - 9 * num_slow_cpu)
Tk = [] Tk = []
for i in range(self.args.num_nodes): for i in range(self.args.num_nodes):
if cpu_type[i] == 0: if cpu_type[i] == 0:
...@@ -52,9 +59,18 @@ class Network: ...@@ -52,9 +59,18 @@ class Network:
Tk.append(10 * (1 - h_k_slow)) Tk.append(10 * (1 - h_k_slow))
for i in range(self.args.num_nodes): for i in range(self.args.num_nodes):
self.nodes.append(Node(self.sim, i, bandwidth_type[i],\ self.nodes.append(
cpu_type[i], float(self.I)/Tk[i], self.args.T_tx,\ Node(
self.args.num_nodes, self.pij)) self.sim,
i,
bandwidth_type[i],
cpu_type[i],
float(self.I) / Tk[i],
self.args.T_tx,
self.args.num_nodes,
self.pij,
)
)
return return
def create_connections(self): def create_connections(self):
...@@ -66,12 +82,20 @@ class Network: ...@@ -66,12 +82,20 @@ class Network:
valid_graph = False valid_graph = False
cnt = 0 cnt = 0
while valid_graph == False: while valid_graph == False:
#print("tyring to create a valid graph",cnt) # print("tyring to create a valid graph",cnt)
adjacency_list = [[] for _ in range(num_nodes)] adjacency_list = [[] for _ in range(num_nodes)]
for i in range(num_nodes): for i in range(num_nodes):
num_connections = random.randint(3, 6) num_connections = random.randint(3, 6)
potential_neighbors = [x for x in range(num_nodes) if x != i and len(adjacency_list[x]) < 6 and x not in adjacency_list[i]] potential_neighbors = [
neighbors = random.sample(potential_neighbors, min(num_connections, len(potential_neighbors))) x
for x in range(num_nodes)
if x != i
and len(adjacency_list[x]) < 6
and x not in adjacency_list[i]
]
neighbors = random.sample(
potential_neighbors, min(num_connections, len(potential_neighbors))
)
for j in neighbors: for j in neighbors:
adjacency_list[i].append(j) adjacency_list[i].append(j)
adjacency_list[j].append(i) adjacency_list[j].append(i)
......
...@@ -2,8 +2,9 @@ from blockchain import Blockchain ...@@ -2,8 +2,9 @@ from blockchain import Blockchain
from simulate import Event from simulate import Event
from utils import sample_exponential, gen_txn_id, get_txn_id from utils import sample_exponential, gen_txn_id, get_txn_id
import random import random
from utils import *
#TODO(SM): Need to bind each transaction with a time of arrival at a node # TODO(SM): Need to bind each transaction with a time of arrival at a node
class Node: class Node:
...@@ -11,6 +12,7 @@ class Node: ...@@ -11,6 +12,7 @@ class Node:
# Initialize node attributes # Initialize node attributes
self.sim = sim self.sim = sim
self.balance = 0 self.balance = 0
# self.nodeId = gen_hash(str(ID))
self.nodeId = ID self.nodeId = ID
self.cpuType = cpu_type self.cpuType = cpu_type
self.bandwidth_type = bandwidth_type self.bandwidth_type = bandwidth_type
...@@ -30,11 +32,20 @@ class Node: ...@@ -30,11 +32,20 @@ class Node:
""" """
# Initialize simulation # Initialize simulation
# schedule the first transaction # schedule the first transaction
self.sim.push_event(Event(self.sim.curr_time + sample_exponential(self.T_tx),\ self.sim.push_event(
self._event_generate_transaction)) Event(
self.sim.curr_time + sample_exponential(self.T_tx),
self.sim.push_event(Event(self.sim.curr_time + sample_exponential(self.Tk),\ self._event_generate_transaction,
self._event_generate_block, self.blockchain.create_new_block_for_pow())) )
)
# self.sim.push_event(
# Event(
# self.sim.curr_time + sample_exponential(self.Tk),
# self._event_generate_block,
# self.blockchain.create_new_block_for_pow(),
# )
# )
def get_latency(self, node, data): def get_latency(self, node, data):
""" """
...@@ -42,22 +53,22 @@ class Node: ...@@ -42,22 +53,22 @@ class Node:
""" """
# check if the bandwidth type is slow for either of the nodes # check if the bandwidth type is slow for either of the nodes
if(self.bandwidth_type == 1 and node.bandwidth_type == 1): if self.bandwidth_type == 1 and node.bandwidth_type == 1:
cij = 100 cij = 100
else: else:
cij = 5 cij = 5
cij *= 1e6 cij *= 1e6
dij = sample_exponential((96*1e3)/cij) dij = sample_exponential((96 * 1e3) / cij)
latency = dij + self.pij latency = dij + self.pij
if(type(data) == str): # transaction if type(data) == str: # transaction
m = 1024 m = 1024
else: else:
# TODO(AG): latency for block # TODO(AG): latency for block
m = 1024 m = 1024
return latency + (float(m)/cij) return latency + (float(m) / cij)
def _event_generate_transaction(self, args): def _event_generate_transaction(self, args):
""" """
...@@ -72,15 +83,19 @@ class Node: ...@@ -72,15 +83,19 @@ class Node:
amount = random.randint(1, self.balance + 10) amount = random.randint(1, self.balance + 10)
# Create a transaction and it to current nodes pending transactions # Create a transaction and it to current nodes pending transactions
txn = str(gen_txn_id()) + ": " + str(self.nodeId) + " pays "\ txn_str = str(self.nodeId) + " pays " + str(dest) + " " + str(amount) + " coins"
+ str(dest) + " " + str(amount) + " coins" txn = str(gen_hash(txn_str)) + ": " + txn_str
print(self.sim.curr_time, self.nodeId, "Generated a txn:", txn) print(self.sim.curr_time, self.nodeId, "Generated a txn:", txn)
self.pending_transactions.append(txn) self.blockchain.pending_transactions.add(txn)
# schedule the next transaction # schedule the next transaction
self.sim.push_event(Event(self.sim.curr_time + sample_exponential(self.T_tx),\ self.sim.push_event(
self._event_generate_transaction)) Event(
self.sim.curr_time + sample_exponential(self.T_tx),
self._event_generate_transaction,
)
)
# broadcast the transaction to the neighbors # broadcast the transaction to the neighbors
self.broadcast_transaction(txn) self.broadcast_transaction(txn)
...@@ -91,15 +106,21 @@ class Node: ...@@ -91,15 +106,21 @@ class Node:
Event to receive a transaction from a neighbor Event to receive a transaction from a neighbor
""" """
txn = txn[0] txn = txn[0]
print(self.sim.curr_time, self.nodeId, "Received transaction", get_txn_id(txn))
# Receive the transaction and add it to the pending transactions # Receive the transaction and add it to the pending transactions
# If the transaction is new, add it and broadcast to the neighbors # If the transaction is new, add it and broadcast to the neighbors
if self.blockchain.receive_txn(txn) == 1: if self.blockchain.received_txn(txn) == 1:
# Broadcast the received transaction to the neighbors immediately # Broadcast the received transaction to the neighbors immediately
print(
self.sim.curr_time, self.nodeId, "Received transaction", get_txn_id(txn)
)
self.broadcast_transaction(txn) self.broadcast_transaction(txn)
else: else:
print(self.sim.curr_time, self.nodeId, "Already exist, discarding ", get_txn_id(txn)) print(
return self.sim.curr_time,
self.nodeId,
"TXN exist, discarding ",
get_txn_id(txn),
)
def broadcast_transaction(self, txn): def broadcast_transaction(self, txn):
""" """
...@@ -107,16 +128,25 @@ class Node: ...@@ -107,16 +128,25 @@ class Node:
""" """
print(self.sim.curr_time, self.nodeId, "Broadcasting txn", get_txn_id(txn)) print(self.sim.curr_time, self.nodeId, "Broadcasting txn", get_txn_id(txn))
for neighbor in self.neighbors: for neighbor in self.neighbors:
print(self.sim.curr_time, self.nodeId, "Sending txn", get_txn_id(txn),\ print(
"to", neighbor.nodeId, self.sim.curr_time\ self.sim.curr_time,
+ self.get_latency(neighbor, txn)) self.nodeId,
"Sending txn",
self.sim.push_event(Event(self.sim.curr_time + self.get_latency(neighbor, txn),\ get_txn_id(txn),
neighbor._event_receive_transaction, txn)) "to",
neighbor.nodeId,
self.sim.curr_time + self.get_latency(neighbor, txn),
)
self.sim.push_event(
Event(
self.sim.curr_time + self.get_latency(neighbor, txn),
neighbor._event_receive_transaction,
txn,
)
)
return return
def _event_generate_block(self, blk): def _event_generate_block(self, blk):
""" """
Event to perform create a block and perform PoW and broadcast it Event to perform create a block and perform PoW and broadcast it
...@@ -132,8 +162,13 @@ class Node: ...@@ -132,8 +162,13 @@ class Node:
# Schedule the next block creation event # Schedule the next block creation event
new_blk = self.blockchain.create_new_block_for_pow() new_blk = self.blockchain.create_new_block_for_pow()
self.sim.push_event(Event(self.sim.curr_time + sample_exponential(self.Tk),\ self.sim.push_event(
self._event_generate_block, new_blk)) Event(
self.sim.curr_time + sample_exponential(self.Tk),
self._event_generate_block,
new_blk,
)
)
return return
def _event_receive_block(self, blk): def _event_receive_block(self, blk):
...@@ -146,7 +181,7 @@ class Node: ...@@ -146,7 +181,7 @@ class Node:
If not valid ignore If not valid ignore
""" """
if self.blockchain.alread_exist(blk) or self.blockchain.verify_blk(blk) == 0: if self.blockchain.already_exist(blk) or self.blockchain.verify_blk(blk) == 0:
return return
self.blockchain.add_new_mined_block(blk) self.blockchain.add_new_mined_block(blk)
...@@ -155,8 +190,13 @@ class Node: ...@@ -155,8 +190,13 @@ class Node:
if chain_length > self.blockchain.len_longest_chain: if chain_length > self.blockchain.len_longest_chain:
self.blockchain.len_longest_chain = self.blockchain.get_chain_length(blk) self.blockchain.len_longest_chain = self.blockchain.get_chain_length(blk)
self.blockchain.head = blk.blkId self.blockchain.head = blk.blkId
self.sim.push_event(Event(self.sim.curr_time + sample_exponential(self.Tk),\ self.sim.push_event(
self._event_generate_block, self.blockchain.create_new_block_for_pow())) Event(
self.sim.curr_time + sample_exponential(self.Tk),
self._event_generate_block,
self.blockchain.create_new_block_for_pow(),
)
)
self.broadcast_block(blk) self.broadcast_block(blk)
...@@ -165,6 +205,14 @@ class Node: ...@@ -165,6 +205,14 @@ class Node:
Broadcast the block to the neighbors Broadcast the block to the neighbors
""" """
for neighbor in self.neighbors: for neighbor in self.neighbors:
self.sim.push_event(Event(self.sim.curr_time + self.get_latency(neighbor, blk),\ self.sim.push_event(
neighbor._event_receive_blk, blk)) Event(
self.sim.curr_time + self.get_latency(neighbor, blk),
neighbor._event_receive_blk,
blk,
)
)
return return
# def __str__(self): # Hashed node Id
# return str(self.nodeId)
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -7,6 +7,7 @@ class Event: ...@@ -7,6 +7,7 @@ class Event:
self.time = time self.time = time
self.operation = operation self.operation = operation
self.args = args self.args = args
def process(self): def process(self):
#print("Processing event at time", self.time) #print("Processing event at time", self.time)
return self.operation(self.args) return self.operation(self.args)
...@@ -30,7 +31,7 @@ class EventSimulator: ...@@ -30,7 +31,7 @@ class EventSimulator:
Start the simulation. Start the simulation.
Run till the simulation time is reached or the queue is empty Run till the simulation time is reached or the queue is empty
""" """
print("Starting the simulation") print("Starting the simulation...")
while len(self.queue) > 0 and self.queue[0].time < self.sim_time: while len(self.queue) > 0 and self.queue[0].time < self.sim_time:
event = self.queue.pop(0) event = self.queue.pop(0)
self.curr_time = event.time self.curr_time = event.time
......
import random import random
import hashlib
GENESIS_SECRET = "afa25t#$e09ad&1q0-9quQ8q2_aE8Q^"
def gen_hash(value):
return hashlib.sha256(value.encode("utf-8")).hexdigest()
def sample_exponential(mean): def sample_exponential(mean):
return random.expovariate(1/mean) return random.expovariate(1 / mean)
txn_id = 0 txn_id = 0
def gen_txn_id(): def gen_txn_id():
global txn_id global txn_id
txn_id += 1 txn_id += 1
return txn_id return txn_id
def get_txn_id(txn): def get_txn_id(txn):
return txn.split(":")[0] return txn.split(":")[0]
blk_id = 0 blk_id = 0
def gen_blk_id(): def gen_blk_id():
global blk_id global blk_id
blk_id += 1 blk_id += 1
return blk_id return blk_id
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment