def add_switch_message(parent_shard, child_to_become_parent, child_to_move_down, position): global mempools mempools[parent_shard].insert(position, {'opcode': 'switch', 'child_to_become_parent': child_to_become_parent, 'child_to_move_down': child_to_move_down}) # Setup GENESIS_BLOCKS = {} GENESIS_MESSAGES = [] for ID in SHARD_IDS: GENESIS_BLOCKS[ID] = Block(ID, sources={}) # temporarily set sources to {}, since genesis blocks are not known yet GENESIS_MESSAGES.append(ConsensusMessage(GENESIS_BLOCKS[ID], 0, [])) # The watcher is the sender of the genesis blocks for ID in SHARD_IDS: GENESIS_BLOCKS[ID].sources = {ID : GENESIS_BLOCKS[ID] for ID in SHARD_IDS} GENESIS_BLOCKS[ID].parent_ID = None for _ in SHARD_IDS: if ID in INITIAL_TOPOLOGY[_]: GENESIS_BLOCKS[ID].parent_ID = _ GENESIS_BLOCKS[ID].child_IDs = INITIAL_TOPOLOGY[ID] for ID in SHARD_IDS: GENESIS_BLOCKS[ID].compute_routing_table() validators = {} for name in VALIDATOR_NAMES: validators[name] = Validator(name) # Watcher lives at validator name 0 and receives all the messages watcher = validators[0]
def make_block(self, shard_ID, mempools, drain_amount, genesis_blocks, TTL=TTL_CONSTANT): genesis_blocks = self.genesis_blocks() # RUN FORK CHOICE RULE ON SELF # will only have fork choices for parent and children my_fork_choice = self.make_fork_choice(shard_ID) # --------------------------------------------------------------------# # GET PREVBLOCK POINTER FROM FORK CHOICE prevblock = my_fork_choice # --------------------------------------------------------------------# # EXTEND THE TRANSACTION LOG FROM THE MEMPOOL prev_txn_log = prevblock.txn_log new_txn_log = copy.copy(prev_txn_log) data = [] num_prev_txs = len(prev_txn_log) neighbor_shard_IDs = [] if my_fork_choice.parent_ID is not None: neighbor_shard_IDs.append(my_fork_choice.parent_ID) for IDs in my_fork_choice.child_IDs: neighbor_shard_IDs.append(IDs) # BUILD SOURCES sources = {ID: genesis_blocks[ID] for ID in SHARD_IDS} for ID in neighbor_shard_IDs: if ID == shard_ID: continue #if len(prevblock.received_log.log[ID]): # assert prevblock.received_log.log[ID][-1].base.shard_ID == ID # sources[ID] = prevblock.received_log.log[ID][-1].base neighbor_fork_choice = self.make_fork_choice(ID) # SOURCES = FORK CHOICE (except for self) sources[ID] = neighbor_fork_choice assert sources[ID].is_in_chain( prevblock.sources[ID] ), "Sources inconsistent, new block shard id: %s, source from shard: %s, heights: %s over %s, new_source_parent_ID: %s, old_source_parent_ID: %s, first_children: %s, three_parent: %s" % ( shard_ID, ID, sources[ID].height, prevblock.sources[ID].height, sources[ID].parent_ID, prevblock.sources[ID].parent_ID, self.make_fork_choice(1).child_IDs, self.make_fork_choice(3).parent_ID) #for ID in SHARD_IDS: # if ID not in neighbor_shard_IDs and ID != shard_ID: # sources[ID] = prevblock.sources[ID] # --------------------------------------------------------------------# if num_prev_txs < len(mempools[shard_ID]) and 'opcode' in mempools[ shard_ID][num_prev_txs]: child_to_become_parent = mempools[shard_ID][num_prev_txs][ 'child_to_become_parent'] child_to_move_down = mempools[shard_ID][num_prev_txs][ 'child_to_move_down'] # TODO: for swapping with root only one message will be needed sources = copy.copy(prevblock.sources) msg1 = SwitchMessage_BecomeAParent(sources[child_to_become_parent], 1, child_to_become_parent, child_to_move_down, sources[child_to_move_down]) msg2 = SwitchMessage_ChangeParent(sources[child_to_move_down], 1, child_to_move_down, child_to_become_parent, sources[child_to_become_parent]) mempools[shard_ID] = mempools[shard_ID][:num_prev_txs] + mempools[ shard_ID][num_prev_txs + 1:] sent_log = copy.copy(prevblock.sent_log) received_log = copy.copy(prevblock.received_log) sent_log.add_message(msg1.target_shard_ID, msg1) sent_log.add_message(msg2.target_shard_ID, msg2) ret = Block(shard_ID, prevblock, new_txn_log, sent_log, received_log, sources, prevblock.vm_state) ret.child_IDs.remove(child_to_move_down) ret.routing_table[child_to_move_down] = child_to_become_parent print("IMPORTANT: %s" % ret.routing_table) return ret for i in range(drain_amount): if num_prev_txs + i < len(mempools[shard_ID]): new_tx = mempools[shard_ID][num_prev_txs + i] if 'opcode' in new_tx: # this is a switch message, stop processing messages break new_txn_log.append(new_tx) data.append(new_tx) # --------------------------------------------------------------------# # BUILD RECEIVED LOG WITH: received_log = MessagesLog() for ID in SHARD_IDS: if ID == shard_ID: continue if ID in neighbor_shard_IDs: neighbor_fork_choice = self.make_fork_choice(ID) # RECEIVED = SENT MESSAGES FROM FORK CHOICE received_log.log[ID] = copy.copy( neighbor_fork_choice.sent_log.log[shard_ID]) else: received_log.log[ID] = copy.copy( prevblock.received_log.log[ID]) # --------------------------------------------------------------------# # PREP NEWLY RECEIVED PMESSAGES IN A RECEIVEDLOG FOR EVM: newly_received_messages = {} new_sent_messages = MessagesLog() for ID in SHARD_IDS: previous_received_log_size = len(prevblock.received_log.log[ID]) newly_received_messages[ID] = received_log.log[ID][ previous_received_log_size:] become_a_parent_of = None change_parent_to = None newly_received_payloads = MessagesLog() for ID in SHARD_IDS: for m in newly_received_messages[ID]: if m.target_shard_ID == shard_ID: if isinstance(m, SwitchMessage_BecomeAParent): become_a_parent_of = (m.new_child_ID, m.new_child_source) elif isinstance(m, SwitchMessage_ChangeParent): change_parent_to = (m.new_parent_ID, m.new_parent_source) routing_table = copy.copy(prevblock.routing_table) new_parent_ID = prevblock.parent_ID if become_a_parent_of is not None: routing_table[become_a_parent_of[0]] = become_a_parent_of[0] neighbor_fork_choice = self.make_fork_choice(become_a_parent_of[0]) sources[become_a_parent_of[0]] = neighbor_fork_choice ID = become_a_parent_of[0] received_log.log[ID] = copy.copy( neighbor_fork_choice.sent_log.log[shard_ID]) previous_received_log_size = len(prevblock.received_log.log[ID]) newly_received_messages[ID] = received_log.log[ID][ previous_received_log_size:] if change_parent_to is not None: new_parent_ID = change_parent_to[0] neighbor_fork_choice = self.make_fork_choice(change_parent_to[0]) sources[change_parent_to[0]] = neighbor_fork_choice ID = change_parent_to[0] received_log.log[ID] = copy.copy( neighbor_fork_choice.sent_log.log[shard_ID]) previous_received_log_size = len(prevblock.received_log.log[ID]) newly_received_messages[ID] = received_log.log[ID][ previous_received_log_size:] for ID in SHARD_IDS: for m in newly_received_messages[ID]: if m.target_shard_ID == shard_ID: if isinstance(m, SwitchMessage_BecomeAParent): become_a_parent_of = (m.new_child_ID, m.new_child_source) elif isinstance(m, SwitchMessage_ChangeParent): change_parent_to = (m.new_parent_ID, m.new_parent_source) else: newly_received_payloads.add_message(ID, m) else: next_hop_ID = self.next_hop(routing_table, m.target_shard_ID) if next_hop_ID is not None: assert next_hop_ID in prevblock.child_IDs, "shard_ID: %s, destination: %s, next_hop: %s, children: %s" % ( shard_ID, ID, next_hop_ID, prevblock.child_IDs) else: next_hop_ID = new_parent_ID assert next_hop_ID is not None new_sent_messages.log[next_hop_ID].append( Message(sources[next_hop_ID], m.TTL, m.target_shard_ID, m.payload)) # --------------------------------------------------------------------# # KEY EVM INTEGRATION HERE # this is where we have this function that produces the new vm state and the new outgoing payloads # new_vm_state, new_outgoing_payloads = apply_to_state(prevblock.vm_state, data, newly_received_payloads) new_vm_state, new_outgoing_payloads = apply_to_state( prevblock.vm_state, data, newly_received_payloads, genesis_blocks) # --------------------------------------------------------------------# # BUILD SENT LOG FROM NEW OUTGOING PAYLOADS # by this time new_sent_messages might already have some messages from rerouting above for ID in SHARD_IDS: if ID != shard_ID: for m in new_outgoing_payloads.log[ID]: # if TTL == 0, then we'll make an invalid block # one that sends a message that must be included by the base # which already exists and therefore cannot include this message if TTL > 0: first_hop_ID = self.next_hop(routing_table, ID) if first_hop_ID is not None: assert first_hop_ID in prevblock.child_IDs, "shard_ID: %s, target: %s, first_hop_ID: %s, parent: %s, children: %s, rtable: %s" % ( shard_ID, ID, first_hop_ID, prevblock.parent_ID, prevblock.child_IDs, prevblock.routing_table) else: first_hop_ID = new_parent_ID assert first_hop_ID is not None new_sent_messages.log[first_hop_ID].append( Message(sources[first_hop_ID], TTL, ID, m.payload)) else: print("Warning: Not sending message because TTL == 0") sent_log = prevblock.sent_log.append_MessagesLog(new_sent_messages) # --------------------------------------------------------------------# ret = Block(shard_ID, prevblock, new_txn_log, sent_log, received_log, sources, new_vm_state, postpone_validation=True) if become_a_parent_of is not None: assert become_a_parent_of[ 0] not in ret.child_IDs, "shard_ID: %s, become_of: %s, child_IDs: %s" % ( shard_ID, become_a_parent_of[0], ret.child_IDs) ret.child_IDs.append(become_a_parent_of[0]) ret.routing_table[become_a_parent_of[0]] = become_a_parent_of[0] if change_parent_to is not None: assert change_parent_to[0] not in ret.child_IDs assert change_parent_to[0] != ret.parent_ID ret.parent_ID = change_parent_to[0] check = ret.is_valid() assert check[0], check[1] return ret