Commit d366f99d authored by Abhishek Kumar's avatar Abhishek Kumar

Completed txns code

parent fb9756fb
from utils import *
from simulate import Event
class Block:
def __init__(self, peer, ID, transactions, previous_block_ID):
def __init__(self, nodeID, ID, transactions, previous_block_ID):
# Initialize block attributes
self.blk_Id = ID
self.transactions = transactions
self.prev_blk_Id = previous_block_ID
self.pow_user_id = peer
self.pow_user_id = nodeID
return
class Blockchain:
def __init__(self):
# Initialize blockchain attributes
# TODO: Should broadcast the genesis block
self.chain = {}
self.genensis_block = Block(0, [], -1)
self.chain[self.genensis_block.blk_Id] = self.genensis_block
self.head = 0
self.genensis_block = Block(-1, gen_hash(GENESIS_SECRET), [], -1)
self.chain[self.genensis_block.blk_Id] = (self.genensis_block, 0) #
self.head = self.genensis_block.blk_Id
self.len_longest_chain = 1
self.seen_transactions = set()
self.pending_transactions = set()
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
if blk_Id is None:
curr = self.len_longest_chain
curr = self.head
curr = blk_Id
cnt = 0
while(curr != -1):
while curr != self.genensis_block.blk_Id:
if curr not in self.chain:
print("ERROR: Block not found in the chain")
break
curr = self.chain[curr].prev_blk_Id
cnt += 1
return cnt
def receive_txn(self, txn):
"""
Receive transaction from a neighbor
if already seen, return 0 else return 1
Receive transaction from a neighbor
if already seen, return 0 else return 1
"""
if txn in self.seen_transactions:
return 0
......@@ -49,18 +50,33 @@ class Blockchain:
self.seen_transactions.add(txn_id)
self.pending_transactions.add(txn)
return 1
def verify_blk(self, blk):
"""
Verify the block
if valid, return 1 else 0
Verify the block
if valid, return 1 else 0
"""
return 1
def add_mined_block(self, blk):
"""
Add the mined block to the blockchain
Add the mined block to the blockchain
"""
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,30 +3,46 @@ from simulate import EventSimulator
import random
from network import Network
def parse_cmd():
"""
Parse command line arguments
"""
parser = argparse.ArgumentParser()
parser.add_argument('-n', '--num_nodes', type = int, default = 50, help="Number of nodes in the network")
parser.add_argument('--z0', type = int, default = 10, help="percentage of node with slow bandwidth")
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('--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")
parser.add_argument(
"-n", "--num_nodes", type=int, default=50, help="Number of nodes in the network"
)
parser.add_argument(
"--z0", type=int, default=10, help="percentage of node with slow bandwidth"
)
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(
"--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()
if __name__ == '__main__':
if __name__ == "__main__":
args = parse_cmd()
random.seed(args.seed)
print(args)
sim = EventSimulator(args)
nx = Network(sim, args)
sim.startSimulation()
exit(0)
\ No newline at end of file
exit(0)
......@@ -3,38 +3,45 @@ import random
from simulate import Event
from utils import sample_exponential
class Network:
def __init__(self, sim, args):
self.args = args
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.create_nodes()
self.create_connections()
self.sim.push_event(Event(0, self.init_simulation,))
self.sim.push_event(
Event(
0,
self.init_simulation,
)
)
return
def init_simulation(self, args):
for node in self.nodes:
node.init_simulation()
return
def create_nodes(self):
"""
Create a list of nodes with the specified parameters
Create a list of nodes with the specified parameters
"""
self.nodes = []
# Create a list of badwidth and cpu type based on z0 and z1
# 0 is slow and 1 is fast
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)
for idx in rand_idx:
bandwidth_type[idx] = 0
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)
for idx in rand_idx:
cpu_type[idx] = 0
......@@ -42,8 +49,8 @@ class Network:
# Based on CPU type calculate the PoW time Tk
# 10 * h_k * (n - num_slow) + h_k * num_slow = 1
# => 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 = []
for i in range(self.args.num_nodes):
if cpu_type[i] == 0:
......@@ -52,26 +59,43 @@ class Network:
Tk.append(10 * (1 - h_k_slow))
for i in range(self.args.num_nodes):
self.nodes.append(Node(self.sim, i, bandwidth_type[i],\
cpu_type[i], float(self.I)/Tk[i], self.args.T_tx,\
self.args.num_nodes, self.pij))
self.nodes.append(
Node(
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
def create_connections(self):
"""
Create a bidirectional graph, 3-6 adjacent nodes per node
TODO(SM): Need a better way to create a valid graph
Create a bidirectional graph, 3-6 adjacent nodes per node
TODO(SM): Need a better way to create a valid graph
"""
num_nodes = self.args.num_nodes
valid_graph = False
cnt = 0
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)]
for i in range(num_nodes):
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]]
neighbors = random.sample(potential_neighbors, min(num_connections, len(potential_neighbors)))
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:
adjacency_list[i].append(j)
adjacency_list[j].append(i)
......@@ -90,4 +114,4 @@ class Network:
for node in self.nodes:
print(node.nodeId, node)
print("Graph created")
return
\ No newline at end of file
return
......@@ -2,15 +2,17 @@ from blockchain import Blockchain
from simulate import Event
from utils import sample_exponential, gen_txn_id, get_txn_id
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:
def __init__(self, sim, ID, bandwidth_type, cpu_type, Tk, T_tx, num_nodes, pij):
# Initialize node attributes
self.sim = sim
self.balance = 0
# self.nodeId = gen_hash(str(ID))
self.nodeId = ID
self.cpuType = cpu_type
self.bandwidth_type = bandwidth_type
......@@ -25,45 +27,54 @@ class Node:
def init_simulation(self):
"""
Initialize the simulation for generatinv transactions
TODO: Schedule Block creation event
Initialize the simulation for generatinv transactions
TODO: Schedule Block creation event
"""
# Initialize simulation
# schedule the first transaction
self.sim.push_event(Event(self.sim.curr_time + sample_exponential(self.T_tx),\
self._event_generate_transaction))
self.sim.push_event(Event(self.sim.curr_time + sample_exponential(self.Tk),\
self._event_generate_block, self.blockchain.create_new_block_for_pow()))
self.sim.push_event(
Event(
self.sim.curr_time + sample_exponential(self.T_tx),
self._event_generate_transaction,
)
)
# 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):
"""
Get the latency to send the transaction to the given node
Get the latency to send the transaction to the given node
"""
# 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
else:
cij = 5
cij *= 1e6
dij = sample_exponential((96*1e3)/cij)
dij = sample_exponential((96 * 1e3) / cij)
latency = dij + self.pij
if(type(data) == str): # transaction
if type(data) == str: # transaction
m = 1024
else:
# TODO(AG): latency for block
m = 1024
return latency + (float(m)/cij)
return latency + (float(m) / cij)
def _event_generate_transaction(self, args):
"""
Generate a transaction for the given peer
Format: "TxnID: IDx pays IDy C coins"
"""
Generate a transaction for the given peer
Format: "TxnID: IDx pays IDy C coins"
"""
# Select a random destination node and amount
dest = self.nodeId
......@@ -72,99 +83,136 @@ class Node:
amount = random.randint(1, self.balance + 10)
# Create a transaction and it to current nodes pending transactions
txn = str(gen_txn_id()) + ": " + str(self.nodeId) + " pays "\
+ str(dest) + " " + str(amount) + " coins"
txn_str = str(self.nodeId) + " pays " + str(dest) + " " + str(amount) + " coins"
txn = str(gen_hash(txn_str)) + ": " + txn_str
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
self.sim.push_event(Event(self.sim.curr_time + sample_exponential(self.T_tx),\
self._event_generate_transaction))
self.sim.push_event(
Event(
self.sim.curr_time + sample_exponential(self.T_tx),
self._event_generate_transaction,
)
)
# broadcast the transaction to the neighbors
self.broadcast_transaction(txn)
pass
def _event_receive_transaction(self, txn):
"""
Event to receive a transaction from a neighbor
Event to receive a transaction from a neighbor
"""
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
# 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
print(
self.sim.curr_time, self.nodeId, "Received transaction", get_txn_id(txn)
)
self.broadcast_transaction(txn)
else:
print(self.sim.curr_time, self.nodeId, "Already exist, discarding ", get_txn_id(txn))
return
print(
self.sim.curr_time,
self.nodeId,
"TXN exist, discarding ",
get_txn_id(txn),
)
def broadcast_transaction(self, txn):
"""
Broadcast the transaction to the neighbors
Broadcast the transaction to the neighbors
"""
print(self.sim.curr_time, self.nodeId, "Broadcasting txn", get_txn_id(txn))
for neighbor in self.neighbors:
print(self.sim.curr_time, self.nodeId, "Sending txn", get_txn_id(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))
print(
self.sim.curr_time,
self.nodeId,
"Sending txn",
get_txn_id(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
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
"""
# discard the event if the time is not right
# this means this event has been overriden
if self.sim.curr_time != self.blockchain.next_pow_completion_time:
return
return
# Add the block it to the blockchain and broadcast it to the neighbors
self.blockchain.add_new_mined_block(blk)
self.broadcast_block(blk)
# Schedule the next block creation event
new_blk = self.blockchain.create_new_block_for_pow()
self.sim.push_event(Event(self.sim.curr_time + sample_exponential(self.Tk),\
self._event_generate_block, new_blk))
self.sim.push_event(
Event(
self.sim.curr_time + sample_exponential(self.Tk),
self._event_generate_block,
new_blk,
)
)
return
def _event_receive_block(self, blk):
"""
Event to receive a block from a neighbor
Receive the block and validate it's transactions / hash
If valid, check if it is the longest chain
If it is the longest chain, add this block to the blockchain
Schedule the next block creation event
If not valid ignore
Event to receive a block from a neighbor
Receive the block and validate it's transactions / hash
If valid, check if it is the longest chain
If it is the longest chain, add this block to the blockchain
Schedule the next block creation event
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
self.blockchain.add_new_mined_block(blk)
print(self.sim.curr_time, self.nodeId, "Received block", blk.blkId)
chain_length = self.blockchain.get_chain_length(blk)
if chain_length > self.blockchain.len_longest_chain:
self.blockchain.len_longest_chain = self.blockchain.get_chain_length(blk)
self.blockchain.head = blk.blkId
self.sim.push_event(Event(self.sim.curr_time + sample_exponential(self.Tk),\
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(),
)
)
self.broadcast_block(blk)
def broadcast_block(self, blk):
"""
Broadcast the block to the neighbors
Broadcast the block to the neighbors
"""
for neighbor in self.neighbors:
self.sim.push_event(Event(self.sim.curr_time + self.get_latency(neighbor, blk),\
neighbor._event_receive_blk, blk))
return
\ No newline at end of file
self.sim.push_event(
Event(
self.sim.curr_time + self.get_latency(neighbor, blk),
neighbor._event_receive_blk,
blk,
)
)
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:
self.time = time
self.operation = operation
self.args = args
def process(self):
#print("Processing event at time", self.time)
return self.operation(self.args)
......@@ -30,7 +31,7 @@ class EventSimulator:
Start the simulation.
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:
event = self.queue.pop(0)
self.curr_time = event.time
......
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):
return random.expovariate(1/mean)
return random.expovariate(1 / mean)
txn_id = 0
txn_id = 0
def gen_txn_id():
global txn_id
txn_id += 1
return txn_id
def get_txn_id(txn):
return txn.split(":")[0]
blk_id = 0
blk_id = 0
def gen_blk_id():
global blk_id
blk_id += 1
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