def main(): for i in range(p.Runs): clock = 0 # set clock to 0 at the start of the simulation if p.hasTrans: if p.Ttechnique == "Light": Transaction.create_transactions_light( ) # generate pending transactions elif p.Ttechnique == "Full": Transaction.create_transactions_full( ) # generate pending transactions Node.generate_gensis_block( ) # generate the gensis block for all miners Scheduler.initial_events() # initiate initial events to start with while not Queue.isEmpty() and clock <= p.simTime: next_event = Queue.get_next_event() clock = next_event.time # move clock to the time of the event Event.run_event(next_event) Queue.remove_event(next_event) Consensus.freshness_preferred( ) # apply the longest chain to resolve the forks Results.calculate( ) # calculate the simulation results (e.g., block statstics and miners' rewards) ########## reset all global variable before the next run ############# Results.reset() # reset all variables used to calculate the results Node.resetState( ) # reset all the states (blockchains) for all nodes in the network print("Percentage of blocks mined by the attacker: ", (p.attacker_blocks / float(p.Runs)))
def run_event(event): # run the event from the event list from Scheduler import Scheduler miner = p.NODES[event.block.miner] minerId = miner.id eventTime = event.time blockDepth = event.block.depth blockId = event.block.id # block id blockPrev = event.block.previous # previous block id blockTime = event.block.timestamp blockTrans = event.block.transactions blockSize = event.block.size ##################################### Block Creation Event ####################################################### if event.type == "create_block": if (blockPrev == miner.last_block().id and miner.currentMode == "public"): miner.mined_blocks += 1 Results.totalBlocks += 1 # count # of total blocks created! if p.hasTrans and p.Ttechnique == "Light": blockTrans, blockSize = Transaction.execute_transactions_light( ) elif p.hasTrans and p.Ttechnique == "Full": blockTrans, blockSize = Transaction.execute_transactions_full( miner, blockTime) event.block.transactions = blockTrans if p.hasUncles: blockUncles = Node.add_uncles( miner) # add uncles to the block event.block.uncles = blockUncles b = Block(blockDepth, blockId, blockPrev, blockTime, minerId, blockTrans, blockSize, []) miner.blockchain.append(b) if p.hasTrans and p.Ttechnique == "Light": Transaction.create_transactions_light( ) # generate transactions #Add the block to the dictionary miner.network_blocks[event.block.id] = b miner.longest_branch_length = blockDepth miner.longest_branches = [blockId] #Add the block to the list of longest branches # if(blockDepth > miner.longest_branch_length): # miner.longest_branch_length = blockDepth # miner.longest_branches = [blockId] # elif(blockDepth == miner.longest_branch_length): # miner.longest_branches += [blockId] currentTime = eventTime event.time = blockTime #Send block to all the other nodes Scheduler.receive_block_event(event) miner.check_switch(time=currentTime, blocktime=blockTime) #attacker in private mode starts building a private block on top of this block if (miner.currentMode == "private"): Scheduler.create_private_block_event(miner, currentTime) if (miner.type == "oracle"): Scheduler.oracle_attack_end_event( minerId, blockTime, b) #node in public mode starts building a regular block else: Scheduler.create_block_event(miner, currentTime) ##### # if(miner.type != "honest"): # print("mined block ", blockId, " at time ", currentTime, " on ", blockPrev) ##################################### Private Block Creation Event ####################################################### elif event.type == "create_private_block": if (miner.currentMode == "private" and miner.privateblockchain != [] and blockPrev == miner.last_private_block().id): #print("depth of mainchain: ", miner.last_block().depth) #print("Depth of private chain: ", miner.privateblockchain[len(miner.privateblockchain)-1].depth) #print("Totalblocks: ", Results.totalBlocks) miner.mined_blocks += 1 Results.totalBlocks += 1 # count # of total blocks created! if p.hasTrans and p.Ttechnique == "Light": blockTrans, blockSize = Transaction.execute_transactions_light( ) elif p.hasTrans and p.Ttechnique == "Full": blockTrans, blockSize = Transaction.execute_transactions_full( miner, blockTime) event.block.transactions = blockTrans if p.hasUncles: blockUncles = Node.add_uncles( miner) # add uncles to the block event.block.uncles = blockUncles b = Block(blockDepth, blockId, blockPrev, blockTime, minerId, blockTrans, blockSize, []) miner.privateblockchain.append(b) if p.hasTrans and p.Ttechnique == "Light": Transaction.create_transactions_light( ) # generate transactions miner.network_blocks[event.block.id] = b currentTime = eventTime # print("new private block ",blockId, " at ", currentTime) eventTime = blockTime miner.check_switch(time=currentTime, blocktime=blockTime) if (miner.currentMode == "public"): #publish block if attack becomes successful #Scheduler.receive_block_event(event) Scheduler.receive_private_blocks_event(event) Scheduler.create_block_event(miner, currentTime) #Add the block to the list of longest branches miner.longest_branch_length = blockDepth miner.longest_branches = [blockId] else: #Create private block if attack continues Scheduler.create_private_block_event(miner, currentTime) ##### # if(miner.type != "honest"): # print("mined private block ", blockId, " at time ", currentTime, " on ", blockPrev) ##################################### Block Receiving Event ####################################################### elif event.type == "receive_block": #print(event.node," recieved: ",blockId) node = p.NODES[event.node] # recipint node.network_blocks[event.block.id] = event.block lastBlockId = node.last_block().id # the id of last block blockUncles = event.block.uncles # if(node.id == 16): # print("recieved block ",blockId," mined at ", blockTime," at ",eventTime, "depth: ", blockDepth) #### case 1: the received block is built on top of the last block according to the recipient's blockchain #### if blockPrev == lastBlockId: #Add the block to the list of longest branches node.longest_branch_length = blockDepth node.longest_branches = [blockId] block = Block(blockDepth, blockId, blockPrev, blockTime, minerId, blockTrans, blockSize, blockUncles) # construct the block node.blockchain.append( block) # append the block to local blockchain if p.hasTrans and p.Ttechnique == "Full": Node.update_transactionsPool(node, event.block) currentTime = eventTime # set the time when to start mining the next block if (node.type != "honest"): #If attacker is in public mode, it checks if it can attack if (node.currentMode == "public"): node.check_switch(time=currentTime) if (node.currentMode == "private"): Scheduler.create_private_block_event( node, currentTime) #If attacker is in private mode, it checks if the attack can continue else: node.check_switch(time=currentTime) #If attack unsuccessful, check the possibility of attacking again if (node.currentMode == "public"): node.check_switch(time=currentTime) if (node.currentMode == "private"): Scheduler.create_private_block_event( node, currentTime) if (node.currentMode == "public"): Scheduler.create_block_event(node, currentTime) #### case 2: the received block is not built on top of the last block #### else: depth = blockDepth + 1 #### 1- if depth of the received block > depth of the last block if (depth > len(node.blockchain)): #Add the block to the list of longest branches node.longest_branch_length = blockDepth node.longest_branches = [blockId] if (node.last_block().miner == 16 and node.id == 0): print("block ", node.last_block().id, " kicked from main chain") Node.update_local_blockchain(node, miner, depth) #Node.update_local_blockchain_new(node,blockId) currentTime = eventTime if (node.type != "honest"): #If attacker is in public mode, check if it can begin attacking if (node.currentMode == "public"): node.check_switch(time=currentTime) #If attack starts, start mining private block if (node.currentMode == "private"): Scheduler.create_private_block_event( node, currentTime) #If attacker is already attacking, check if it becomes unsuccessful else: node.check_switch(time=currentTime) #If attack unsuccessful, check the possibility of attacking again if (node.currentMode == "public"): node.check_switch(time=currentTime) if (node.currentMode == "private"): Scheduler.create_private_block_event( node, currentTime) if (node.currentMode == "public"): Scheduler.create_block_event(node, currentTime) #### 2- if depth of the received block = depth of the last block elif (depth == len(node.blockchain)): node.longest_branches.append(blockId) #code to check main chain if (p.ConsensusType == "FP"): # freshest_branch = node.longest_branches[0] # freshest_timestamp = node.network_blocks[node.longest_branches[0]].timestamp # for branch in node.longest_branches[1:]: # if(node.network_blocks[branch].timestamp > freshest_timestamp): # freshest_branch = branch # freshest_timestamp = node.network_blocks[branch].timestamp # #update based on freshest branch # if(lastBlockId != freshest_branch): #If received block is fresher, update local blockchain if (node.last_block().timestamp < blockTime): # if(minerId == 16 and node.id == 0): # print("block ", blockId, " accepted. timestamp: ", blockTime) Node.update_local_blockchain(node, miner, depth) #Node.update_local_blockchain_new(node,freshest_branch) currentTime = eventTime if (node.currentMode == "public"): Scheduler.create_block_event(node, currentTime) else: # if(minerId == 16 and node.id == 0): # print("block ", blockId, " not accepted in main chain FP") uncle = Block(blockDepth, blockId, blockPrev, blockTime, minerId, [], 0, []) # construct the uncle block node.unclechain.append(uncle) if (p.ConsensusType == "FORTIS"): #There should be at least 2 longest branches if (len(node.longest_branches) == 1): print("Error") #choose fresher branch with probability fortis_prob when there are two longest branches if (len(node.longest_branches) == 2): if ((blockTime <= node.last_block().timestamp) ^ (random.random() <= p.fortis_prob)): Node.update_local_blockchain( node, miner, depth) #Node.update_local_blockchain_new(node,freshest_branch) currentTime = eventTime if (node.currentMode == "public"): Scheduler.create_block_event( node, currentTime) else: uncle = Block(blockDepth, blockId, blockPrev, blockTime, minerId, [], 0, []) # construct the uncle block node.unclechain.append(uncle) #Choose any branch with a uniform probability if there are more than 2 longest branches else: branch = random.choice(node.longest_branches) if (node.last_block().id != branch): block = node.network_blocks[branch] miner = p.NODES[block.miner] #longest_branch not being updated properly Node.update_local_blockchain( node, miner, depth) #Node.update_local_blockchain_new(node,random.choice(longest_branches)) #if received block is not chosen, add it to uncle chain elif (branch != blockId): uncle = Block(blockDepth, blockId, blockPrev, blockTime, minerId, [], 0, []) # construct the uncle block node.unclechain.append(uncle) #If no consensus algorithm is active, add it to uncle chain else: uncle = Block(blockDepth, blockId, blockPrev, blockTime, minerId, [], 0, []) # construct the uncle block node.unclechain.append(uncle) #code to update local chain #### 3- if depth of the received block < depth of the last block, then reject the block (add it to unclechain) #### else: # if(minerId == 16 and node.id == 0): # print("block ", blockId, " not accepted in main chain(not deep enough). Chain depth: ", len(node.blockchain), " block depth: ",depth) # for i in node.blockchain: # print ("id: ", i.id, " timestamp: ", i.timestamp, " miner: ", i.miner, "prev: ", i.previous) uncle = Block(blockDepth, blockId, blockPrev, blockTime, minerId, [], 0, []) # construct the uncle block node.unclechain.append(uncle) if p.hasUncles: Node.update_unclechain(node) if p.hasTrans and p.Ttechnique == "Full": Node.update_transactionsPool(node, event.block) # not sure yet. ############################ Attack end Event ################################ elif (event.type == "oracle_attack_end"): currentTime = eventTime miner.currentMode = "public" if (len(miner.privateblockchain) >= len(miner.blockchain)): # print("successful attack at ", currentTime) miner.blockchain = [] miner.blockchain = miner.privateblockchain.copy() event.block = miner.privateblockchain[-1] if (event.block.depth > miner.longest_branch_length): miner.longest_branch_length = blockDepth miner.longest_branches = [event.block.id] elif (event.block.depth == miner.longest_branch_length): miner.longest_branches += [event.block.id] Scheduler.receive_private_blocks_event(event) miner.successfulAttacks += 1 else: # print("unsuccessful attack") miner.unsuccessfulAttacks += 1 Scheduler.create_block_event(miner, currentTime) # print("Switched to public mode") miner.privateblockchain = []