def add(self, block: Block) -> bool: log("BLOCKCHAIN {} ADDING {}".format(self, block)) if block.difficulty < self.difficulty: log("BLOCKCHAIN {} REJECT {} TOO LIGHT {} < {}".format(self, block, block.difficulty, self.difficulty)) return False if block.previous.id != self.tip.id: if block.previous.id in self.blocks: log("BLOCKCHAIN {} REJECT {} STALE".format(self, block)) else: log("BLOCKCHAIN {} REJECT {} UNKNOWN TIP {}".format(self, block, block.previous.id)) return False log("BLOCKCHAIN {} ACCEPT {}".format(self, block)) self.blocks[block.id] = block self.heights.append(block.id) self.height += 1 self.weight += block.difficulty # if self.height % self.difficulty_readjustment_period == 0: # self.readjust_difficulty() return True
def readjust_difficulty(self): # # By construction, # # old_difficulty new_difficulty # ------------------- = observed hashrate = ----------------- # observed block time target block time # # so new_difficulty = (target block time * old_difficulty) / (observed block time) # log("BLOCKCHAIN {} DIFF READJ AT BLOCK {}".format(self, self.height)) blocks = [self.blocks[block_id] for block_id in list(reversed(self.heights))[:self.difficulty_readjustment_period]] block_gaps = [(blocks[index+1].time - block.time) for index, block in enumerate(blocks[:-1])] observed_block_time = mean(block_gaps) old_difficulty = self.difficulty new_difficulty = (self.block_time * old_difficulty) / observed_block_time difficulty_change_ratio = new_difficulty / old_difficulty if difficulty_change_ratio > self.max_difficulty_change_factor: difficulty_change_ratio = self.max_difficulty_change_factor elif difficulty_change_ratio < self.inverse_max_difficulty_change_factor: difficulty_change_ratio = self.inverse_max_difficulty_change_factor self.difficulty = old_difficulty * difficulty_change_ratio log("BLOCKCHAIN {} DIFF. ADJ. {} => {}".format(self, old_difficulty, self.difficulty))
def merge(self, other: 'Blockchain') -> bool: log("BLOCKCHAIN {} MERGING {}".format(self, other)) assert other.chain_params == self.chain_params if other.weight < self.weight: log("BLOCKCHAIN {} REJECT {} AS LIGHTER CHAIN".format(self, other)) return False log("BLOCKCHAIN {} ACCEPT {}".format(self, other)) self.blocks = {block_id:block for (block_id, block) in other.blocks.items()} self.heights = [block_id for block_id in other.heights] self.height = other.height self.weight = other.weight self.difficulty = other.difficulty return True
def receive(self, time, transmission): log("AGENT {} RECEIVE {}".format(self.id, transmission.id)) self.transmissions_received[time] = transmission
def log_advance(self, duration): log("AGENT {} ADVANCE w/ {}".format(self.id, [ transmission.id for transmission in self.transmissions_received.values() ]))