async def set_target(self, to_time): if not self.block_factory.special_min: await self.set_target_from_last_non_special_min( self.config.LatestBlock.block) # todo: keep block target at normal target, for header and block info. # Only tweak target at validation time, and don't include special_min into header if self.block_factory.index >= CHAIN.SPECIAL_MIN_FORK: # TODO: use a CHAIN constant # print("test target", int(to_time), self.last_block_time) if self.block_factory.target == 0: # If the node is started when the current block is special_min, then we have a 0 target await self.set_target_as_previous_non_special_min() # print('target set to', self.block_factory.target) delta_t = int(to_time) - self.last_block_time if delta_t \ > CHAIN.special_min_trigger(self.config.network, self.block_factory.index): special_target = CHAIN.special_target( self.block_factory.index, self.block_factory.target, delta_t, self.config.network) self.block_factory.special_min = True self.block_factory.special_target = special_target self.block_factory.time = int(to_time) else: self.block_factory.special_min = False elif self.block_factory.index < CHAIN.SPECIAL_MIN_FORK: # TODO: use a CHAIN constant if (int(to_time) - self.last_block_time) > self.target_block_time: self.block_factory.target = self.max_target self.block_factory.special_min = True self.block_factory.time = int(to_time) else: self.block_factory.special_min = False
def verify(self): getcontext().prec = 8 if int(self.version) != int(CHAIN.get_version_for_height(self.index)): raise Exception("Wrong version for block height", self.version, CHAIN.get_version_for_height(self.index)) txns = self.get_transaction_hashes() verify_merkle_root = self.get_merkle_root(txns) if verify_merkle_root != self.merkle_root: raise Exception("Invalid block merkle root") header = self.generate_header() hashtest = self.generate_hash_from_header(self.index, header, str(self.nonce)) # print("header", header, "nonce", self.nonce, "hashtest", hashtest) if self.hash != hashtest: getLogger("tornado.application").warning("Verify error hashtest {} header {} nonce {}".format(hashtest, header, self.nonce)) raise Exception('Invalid block hash') address = P2PKHBitcoinAddress.from_pubkey(bytes.fromhex(self.public_key)) try: # print("address", address, "sig", self.signature, "pubkey", self.public_key) result = verify_signature(base64.b64decode(self.signature), self.hash.encode('utf-8'), bytes.fromhex(self.public_key)) if not result: raise Exception("block signature1 is invalid") except: try: result = VerifyMessage(address, BitcoinMessage(self.hash.encode('utf-8'), magic=''), self.signature) if not result: raise except: raise Exception("block signature2 is invalid") # verify reward coinbase_sum = 0 for txn in self.transactions: if int(self.index) > CHAIN.CHECK_TIME_FROM and (int(txn.time) > int(self.time) + CHAIN.TIME_TOLERANCE): #yadacoin.core.config.CONFIG.mongo.db.miner_transactions.remove({'id': txn.transaction_signature}, multi=True) #raise Exception("Block embeds txn too far in the future") pass if txn.coinbase: for output in txn.outputs: coinbase_sum += float(output.value) fee_sum = 0.0 for txn in self.transactions: if not txn.coinbase: fee_sum += float(txn.fee) reward = CHAIN.get_block_reward(self.index) #if Decimal(str(fee_sum)[:10]) != Decimal(str(coinbase_sum)[:10]) - Decimal(str(reward)[:10]): """ KO for block 13949 0.02099999 50.021 50.0 Integrate block error 1 ('Coinbase output total does not equal block reward + transaction fees', 0.020999999999999998, 0.021000000000000796) """ if quantize_eight(fee_sum) != quantize_eight(coinbase_sum - reward): print(fee_sum, coinbase_sum, reward) raise Exception("Coinbase output total does not equal block reward + transaction fees", fee_sum, (coinbase_sum - reward))
async def sync_bottom_up(self): #bottom up syncing last_latest = self.latest_block self.latest_block = self.config.LatestBlock.block if last_latest: if self.latest_block.index > last_latest.index: self.app_log.info( 'Block height: %s | time: %s' % (self.latest_block.index, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))) latest_consensus = await self.mongo.async_db.consensus.find_one({ 'index': self.latest_block.index + 1, 'block.version': CHAIN.get_version_for_height(self.latest_block.index + 1), 'ignore': { '$ne': True } }) if latest_consensus: self.remove_pending_transactions_now_in_chain(latest_consensus) self.remove_fastgraph_transactions_now_in_chain(latest_consensus) latest_consensus = await Block.from_dict(latest_consensus['block']) if self.debug: self.app_log.info("Latest consensus_block {}".format( latest_consensus.index)) records = await self.mongo.async_db.consensus.find({ 'index': self.latest_block.index + 1, 'block.version': CHAIN.get_version_for_height(self.latest_block.index + 1), 'ignore': { '$ne': True } }).to_list(length=100) for record in sorted(records, key=lambda x: int(x['block']['target'], 16)): try: block = await Block.from_dict(record['block']) except: continue blockchain = await Blockchain.init_async([block]) if await self.integrate_blockchain_with_existing_chain( blockchain): return True else: # this path should be for syncing only. # Stack: # search_network_for_new # request_blocks # getblocks <--- rpc request # blocksresponse <--- rpc response # integrate_blockchain_with_existing_chain return await self.search_network_for_new()
async def integrate_block_with_existing_chain(self, block: Block, extra_blocks=None): """Even in case of retrace, this is the only place where we insert a new block into the block collection and update BU""" self.app_log.warning('integrate_block_with_existing_chain') try: # TODO: reorg the checks, to have the faster ones first. # Like, here we begin with checking every tx one by one, when <e did not even check index and provided hash matched previous one. bc = await Blockchain.init_async() result = await bc.test_block(block) if not result: return False # self.mongo.db.blocks.update({'index': block.index}, block.to_dict(), upsert=True) # self.mongo.db.blocks.remove({'index': {"$gt": block.index}}, multi=True) # todo: is this useful? can we have more blocks above? No because if we had, we would have raised just above await self.mongo.async_db.blocks.delete_many( {'index': { "$gte": block.index }}) db_block = block.to_dict() db_block['updated_at'] = time() await self.mongo.async_db.blocks.replace_one( {'index': block.index}, db_block, upsert=True) await self.mongo.async_db.miner_transactions.delete_many({ 'id': { '$in': [x.transaction_signature for x in block.transactions] } }) await self.config.LatestBlock.update_latest_block() self.app_log.info("New block inserted for height: {}".format( block.index)) latest_consensus = await self.mongo.async_db.consensus.find_one({ 'index': block.index + 1, 'block.version': CHAIN.get_version_for_height(block.index + 1), 'ignore': { '$ne': True } }) if not latest_consensus: await self.config.LatestBlock.block_checker( ) # This will trigger mining pool to generate a new block to mine if self.config.mp: await self.config.mp.refresh() await StratumServer.block_checker() if not self.syncing: await self.config.nodeShared().send_block( self.config.LatestBlock.block) return True except Exception as e: from traceback import format_exc self.app_log.warning("{}".format(format_exc()))
async def get_previous_consensus_block_from_local(self, block): #table cleanup new_blocks = self.mongo.async_db.consensus.find({ 'block.hash': block.prev_hash, 'block.index': (block.index - 1), 'block.version': CHAIN.get_version_for_height((block.index - 1)) }) async for new_block in new_blocks: new_block = await Block.from_dict(new_block['block']) yield new_block
async def init_async(cls): self = cls() self.config = get_config() self.mongo = self.config.mongo self.app_log = getLogger("tornado.application") self.target_block_time = CHAIN.target_block_time(self.config.network) self.max_target = CHAIN.MAX_TARGET self.inbound = {} self.connected_ips = {} self.last_block_time = 0 self.index = 0 last_block = await self.config.BU.get_latest_block() if last_block: self.last_block_time = int(last_block['time']) self.index = last_block['index'] self.last_refresh = 0 self.block_factory = None await self.refresh() return self
async def generate( cls, transactions=None, public_key=None, private_key=None, force_version=None, index=0, force_time=None, prev_hash=None, nonce=None, target=CHAIN.MAX_TARGET ): config = get_config() app_log = getLogger("tornado.application") if force_version is None: version = CHAIN.get_version_for_height(index) else: version = force_version if force_time: xtime = str(int(force_time)) else: xtime = str(int(time.time())) index = int(index) if index == 0: prev_hash = '' elif prev_hash is None and index != 0: prev_hash = LatestBlock.block.hash transactions = transactions or [] transaction_objs = [] fee_sum = 0.0 used_sigs = [] used_inputs = {} for txn in transactions: try: if isinstance(txn, Transaction): transaction_obj = txn else: transaction_obj = Transaction.from_dict(txn) if transaction_obj.transaction_signature in used_sigs: print('duplicate transaction found and removed') continue await transaction_obj.verify() used_sigs.append(transaction_obj.transaction_signature) except: raise InvalidTransactionException("invalid transactions") try: if int(index) > CHAIN.CHECK_TIME_FROM and (int(transaction_obj.time) > int(xtime) + CHAIN.TIME_TOLERANCE): config.mongo.db.miner_transactions.remove({'id': transaction_obj.transaction_signature}, multi=True) app_log.debug("Block embeds txn too far in the future {} {}".format(xtime, transaction_obj.time)) continue if transaction_obj.inputs: failed = False used_ids_in_this_txn = [] for x in transaction_obj.inputs: if config.BU.is_input_spent(x.id, transaction_obj.public_key): failed = True if x.id in used_ids_in_this_txn: failed = True if (x.id, transaction_obj.public_key) in used_inputs: failed = True used_inputs[(x.id, transaction_obj.public_key)] = transaction_obj used_ids_in_this_txn.append(x.id) if failed: continue transaction_objs.append(transaction_obj) fee_sum += float(transaction_obj.fee) except Exception as e: await config.mongo.async_db.miner_transactions.delete_many({'id': transaction_obj.transaction_signature}) config.app_log.debug('Exception {}'.format(e)) continue block_reward = CHAIN.get_block_reward(index) coinbase_txn = await Transaction.generate( public_key=public_key, private_key=private_key, outputs=[{ 'value': block_reward + float(fee_sum), 'to': str(P2PKHBitcoinAddress.from_pubkey(bytes.fromhex(public_key))) }], coinbase=True ) transaction_objs.append(coinbase_txn) transactions = transaction_objs block = await cls.init_async( version=version, block_time=xtime, block_index=index, prev_hash=prev_hash, transactions=transactions, public_key=public_key, target=target ) txn_hashes = block.get_transaction_hashes() block.set_merkle_root(txn_hashes) block.target = target block.header = block.generate_header() if nonce: block.nonce = str(nonce) block.hash = block.generate_hash_from_header( block.index, block.header, str(block.nonce) ) block.signature = TU.generate_signature(block.hash, private_key) return block
async def test_block(self, block, extra_blocks=[], simulate_last_block=None): try: block.verify() except Exception as e: self.config.app_log.warning( "Integrate block error 1: {}".format(e)) return False async def get_txns(txns): for x in txns: yield x async def get_inputs(inputs): for x in inputs: yield x if block.index == 0: return True if simulate_last_block: last_block = simulate_last_block else: last_block_data = await self.config.mongo.async_db.blocks.find_one( {'index': block.index - 1}) if last_block_data: last_block = await Block.from_dict(last_block_data) else: return False if block.index >= CHAIN.FORK_10_MIN_BLOCK: target = await CHAIN.get_target_10min(block.index, last_block, block, extra_blocks) else: target = await CHAIN.get_target(block.index, last_block, block, extra_blocks) delta_t = int(time()) - int(last_block.time) special_target = CHAIN.special_target(block.index, block.target, delta_t, get_config().network) if block.index >= 35200 and delta_t < 600 and block.special_min: return False used_inputs = {} i = 0 async for transaction in get_txns(block.transactions): if extra_blocks: transaction.extra_blocks = extra_blocks self.config.app_log.warning('verifying txn: {} block: {}'.format( i, block.index)) i += 1 try: await transaction.verify() except InvalidTransactionException as e: self.config.app_log.warning(e) return False except InvalidTransactionSignatureException as e: self.config.app_log.warning(e) return False except MissingInputTransactionException as e: self.config.app_log.warning(e) return False except NotEnoughMoneyException as e: self.config.app_log.warning(e) return False except Exception as e: self.config.app_log.warning(e) return False if transaction.inputs: failed = False used_ids_in_this_txn = [] async for x in get_inputs(transaction.inputs): txn = self.config.BU.get_transaction_by_id(x.id, instance=True) if not txn: txn = await transaction.find_in_extra_blocks(x) if not txn: failed = True if self.config.BU.is_input_spent(x.id, transaction.public_key, from_index=block.index): failed = True if x.id in used_ids_in_this_txn: failed = True if (x.id, transaction.public_key) in used_inputs: failed = True used_inputs[(x.id, transaction.public_key)] = transaction used_ids_in_this_txn.append(x.id) if failed and block.index >= CHAIN.CHECK_DOUBLE_SPEND_FROM: return False elif failed and block.index < CHAIN.CHECK_DOUBLE_SPEND_FROM: continue if block.index >= 35200 and delta_t < 600 and block.special_min: self.config.app_log.warning( f'Failed: {block.index} >= {35200} and {delta_t} < {600} and {block.special_min}' ) return False if int(block.index) > CHAIN.CHECK_TIME_FROM and int(block.time) < int( last_block.time): self.config.app_log.warning( f'Failed: {int(block.index)} > {CHAIN.CHECK_TIME_FROM} and {int(block.time)} < {int(last_block.time)}' ) return False if last_block.index != (block.index - 1) or last_block.hash != block.prev_hash: self.config.app_log.warning( f'Failed: {last_block.index} != {(block.index - 1)} or {last_block.hash} != {block.prev_hash}' ) return False if int(block.index) > CHAIN.CHECK_TIME_FROM and ( int(block.time) < (int(last_block.time) + 600)) and block.special_min: self.config.app_log.warning( f'Failed: {int(block.index)} > {CHAIN.CHECK_TIME_FROM} and ({int(block.time)} < ({int(last_block.time)} + {600})) and {block.special_min}' ) return False target_block_time = CHAIN.target_block_time(self.config.network) checks_passed = False if (block.index >= CHAIN.BLOCK_V5_FORK) and int( block.little_hash(), 16) < target: self.config.app_log.warning('5') checks_passed = True elif (int(block.hash, 16) < target): self.config.app_log.warning('6') checks_passed = True elif (block.special_min and int(block.hash, 16) < special_target): self.config.app_log.warning('7') checks_passed = True elif (block.special_min and block.index < 35200): self.config.app_log.warning('8') checks_passed = True elif (block.index >= 35200 and block.index < 38600 and block.special_min and (int(block.time) - int(last_block.time)) > target_block_time): self.config.app_log.warning('9') checks_passed = True else: self.config.app_log.warning( "Integrate block error - index and time error") if not checks_passed: return False return True
async def get(self): def get_ticker(): return requests.get('https://safe.trade/api/v2/peatio/public/markets/tickers') try: if not hasattr(self.config, 'ticker'): self.config.ticker = get_ticker() self.config.last_update = time.time() if (time.time() - self.config.last_update) > (600 * 6): self.config.ticker = get_ticker() self.config.last_update = time.time() last_btc = self.config.ticker.json()['ydabtc']['ticker']['last'] except: last_btc = 0 await self.config.LatestBlock.block_checker() pool_public_key = self.config.pool_public_key if hasattr(self.config, 'pool_public_key') else self.config.public_key total_blocks_found = await self.config.mongo.async_db.blocks.count_documents( { 'public_key': pool_public_key } ) expected_blocks = 144 pool_blocks_found_list = await self.config.mongo.async_db.blocks.find( { 'public_key': pool_public_key, 'time': {'$gte': time.time() - ( 600 * 144 )} }, { '_id': 0 } ).sort([('index', -1)]).to_list(100) expected_blocks = 144 mining_time_interval = 240 shares_count = await self.config.mongo.async_db.shares.count_documents({'time': {'$gte': time.time() - mining_time_interval}}) if shares_count > 0: pool_hash_rate = (shares_count * 69905) / mining_time_interval else: pool_hash_rate = 0 net_blocks_found = self.config.mongo.async_db.blocks.find({'time': {'$gte': time.time() - ( 600 * 144 )}}) net_blocks_found = await net_blocks_found.to_list(length=expected_blocks*10) if len(net_blocks_found) > 0: avg_net_target = 0 for block in net_blocks_found: avg_net_target += int(block['target'], 16) avg_net_target = avg_net_target / len(net_blocks_found) net_difficulty = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffff / avg_net_target network_hash_rate = ((len(net_blocks_found)/expected_blocks)*net_difficulty * 2**32 / 600) else: net_difficulty = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffff / 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffff network_hash_rate = 0 miner_count_pool_stat = await self.config.mongo.async_db.pool_stats.find_one({'stat': 'miner_count'}) worker_count_pool_stat = await self.config.mongo.async_db.pool_stats.find_one({'stat': 'worker_count'}) payouts = await self.config.mongo.async_db.share_payout.find({}, {'_id': 0}).sort([('index', -1)]).to_list(100) self.render_as_json({ 'pool': { 'hashes_per_second': pool_hash_rate, 'miner_count': miner_count_pool_stat['value'], 'worker_count': worker_count_pool_stat['value'], 'payout_scheme': 'PPLNS', 'pool_fee': self.config.pool_take, 'min_payout': 0, 'url': getattr(self.config, 'pool_url', f'{self.config.peer_host}:{self.config.stratum_pool_port}'), 'last_five_blocks': [{'timestamp': x['time'], 'height': x['index']} for x in pool_blocks_found_list[:5]], 'blocks_found': total_blocks_found, 'fee': self.config.pool_take, 'payout_frequency': self.config.payout_frequency, 'payouts': payouts, 'blocks': pool_blocks_found_list[:100] }, 'network': { 'height': self.config.LatestBlock.block.index, 'reward': CHAIN.get_block_reward(self.config.LatestBlock.block.index), 'last_block': self.config.LatestBlock.block.time, 'hashes_per_second': network_hash_rate, 'difficulty': net_difficulty }, 'market': { 'last_btc': last_btc }, 'coin': { 'algo': 'randomx YDA', 'circulating': self.config.LatestBlock.block.index * 50, 'max_supply': 21000000 } })
async def on_miner_nonce(self, nonce: str, job: Job, address: str = '') -> bool: nonce = nonce + job.extra_nonce.encode().hex() hash1 = self.block_factory.generate_hash_from_header( self.block_factory.index, self.block_factory.header, nonce) if self.block_factory.index >= CHAIN.BLOCK_V5_FORK: hash1_test = self.little_hash(hash1) else: hash1_test = hash1 if (int(hash1_test, 16) > self.block_factory.target and self.config.network != 'regnet' and (self.block_factory.special_min and int(hash1, 16) > self.block_factory.special_target)): return False block_candidate = await self.block_factory.copy() block_candidate.hash = hash1 block_candidate.nonce = nonce if block_candidate.special_min: delta_t = int(block_candidate.time) - int(self.last_block_time) special_target = CHAIN.special_target(block_candidate.index, block_candidate.target, delta_t, self.config.network) block_candidate.special_target = special_target if (block_candidate.index >= 35200 and (int(block_candidate.time) - int(self.last_block_time)) < 600 and block_candidate.special_min): self.app_log.warning( "Special min block too soon: hash {} header {} nonce {}". format(block_candidate.hash, block_candidate.header, block_candidate.nonce)) return False accepted = False if ((int(block_candidate.target) + 0x0000F00000000000000000000000000000000000000000000000000000000000 ) > int(hash1, 16) or (block_candidate.index >= CHAIN.BLOCK_V5_FORK and (int(block_candidate.target) + 0x0000F00000000000000000000000000000000000000000000000000000000000 ) > int(block_candidate.little_hash(), 16))): # submit share only now, not to slow down if we had a block await self.mongo.async_db.shares.update_one( {'hash': block_candidate.hash}, { '$set': { 'address': address, 'index': block_candidate.index, 'hash': block_candidate.hash, 'nonce': nonce, 'time': int(time()) } }, upsert=True) accepted = True if (int(block_candidate.target) > int(block_candidate.hash, 16) or (block_candidate.index >= CHAIN.BLOCK_V5_FORK and int(block_candidate.target) > int( block_candidate.little_hash(), 16))): block_candidate.signature = self.config.BU.generate_signature( block_candidate.hash, self.config.private_key) try: block_candidate.verify() except Exception as e: self.app_log.warning( "Verify error {} - hash {} header {} nonce {}".format( e, block_candidate.hash, block_candidate.header, block_candidate.nonce)) return False # accept winning block await self.accept_block(block_candidate) # Conversion to dict is important, or the object may change self.app_log.debug('block ok') return { 'hash': block_candidate.hash, 'nonce': nonce, 'height': block_candidate.index, 'id': block_candidate.signature } elif (block_candidate.special_min and (int(block_candidate.special_target) > int( block_candidate.hash, 16)) or (block_candidate.index >= CHAIN.BLOCK_V5_FORK and block_candidate.special_min and (int(block_candidate.special_target) > int( block_candidate.little_hash(), 16)))): block_candidate.signature = self.config.BU.generate_signature( block_candidate.hash, self.config.private_key) try: block_candidate.verify() except Exception as e: self.app_log.warning( "Verify error {} - hash {} header {} nonce {}".format( e, block_candidate.hash, block_candidate.header, block_candidate.nonce)) return False # accept winning block await self.accept_block(block_candidate) # Conversion to dict is important, or the object may change self.app_log.debug('block ok - special_min') return { 'hash': block_candidate.hash, 'nonce': nonce, 'height': block_candidate.index, 'id': block_candidate.signature } if accepted: return { 'hash': block_candidate.hash, 'nonce': nonce, 'height': block_candidate.index, 'id': block_candidate.signature }
async def get(self): def get_ticker(): return requests.get( 'https://safe.trade/api/v2/peatio/public/markets/tickers') try: if not hasattr(self.config, 'ticker'): self.config.ticker = get_ticker() self.config.last_update = time.time() if (time.time() - self.config.last_update) > (600 * 6): self.config.ticker = get_ticker() self.config.last_update = time.time() last_btc = self.config.ticker.json()['ydabtc']['ticker']['last'] except: last_btc = 0 shares_count = await self.config.mongo.async_db.shares.count_documents( {'index': { '$gte': self.config.LatestBlock.block.index - 10 }}) blocks_found = await self.config.mongo.async_db.share_payout.count_documents( {}) last_block_found_payout = await self.config.mongo.async_db.share_payout.find_one( {}, sort=[('index', -1)]) if last_block_found_payout: last_block_found = await self.config.mongo.async_db.blocks.find_one( {'index': last_block_found_payout['index']}) else: last_block_found = None prev_block = await self.config.mongo.async_db.blocks.find_one( {'index': self.config.LatestBlock.block.index - 10}) seconds_elapsed = int(self.config.LatestBlock.block.time) - int( prev_block['time']) expected_blocks = 144 difficulty = CHAIN.MAX_TARGET_V3 / self.config.LatestBlock.block.target net_blocks_found = self.config.mongo.async_db.blocks.find({ 'index': { '$gte': self.config.LatestBlock.block.index - 288 } }).sort([('index', -1)]) include_blocks = [] async for x in net_blocks_found: if int(x['time']) > time.time() - 600: include_blocks.append(x) network_hash_rate = ((len(include_blocks) / expected_blocks) * difficulty * 2**32 / 600) self.render_as_json({ 'pool': { 'hashes_per_second': (shares_count * 1000) / float( seconds_elapsed / 60 ), # it takes 1000H/s to produce 1 0x0000f... share per minute 'miner_count': len(self.config.poolServer.inbound_streams['Miner'].keys()), 'last_block': last_block_found['time'] if last_block_found else 0, 'payout_scheme': 'PPLNS', 'pool_fee': self.config.pool_take, 'blocks_found': blocks_found, 'min_payout': 0, 'url': f'{self.config.peer_host}:{self.config.stratum_pool_port}' }, 'network': { 'height': self.config.LatestBlock.block.index, 'reward': CHAIN.get_block_reward(self.config.LatestBlock.block.index), 'last_block': self.config.LatestBlock.block.time, 'hashes_per_second': network_hash_rate }, 'market': { 'last_btc': last_btc } })
async def insert_block(self, block, stream): self.app_log.debug('insert_block') try: await self.mongo.async_db.blocks.delete_many( {'index': { "$gte": block.index }}) db_block = block.to_dict() db_block['updated_at'] = time() await self.mongo.async_db.blocks.replace_one( {'index': block.index}, db_block, upsert=True) await self.mongo.async_db.miner_transactions.delete_many({ 'id': { '$in': [x.transaction_signature for x in block.transactions] } }) await self.config.LatestBlock.update_latest_block() self.app_log.info("New block inserted for height: {}".format( block.index)) latest_consensus = await self.mongo.async_db.consensus.find_one({ 'index': block.index + 1, 'block.version': CHAIN.get_version_for_height(block.index + 1), 'ignore': { '$ne': True } }) if not latest_consensus: await self.config.LatestBlock.block_checker( ) # This will trigger mining pool to generate a new block to mine if not self.syncing: if stream and stream.syncing: return True await self.config.nodeShared.send_block( self.config.LatestBlock.block) if self.config.mp: if self.syncing: return True try: await self.config.mp.refresh() except Exception as e: self.app_log.warning("{}".format(format_exc())) try: await StratumServer.block_checker() except Exception as e: self.app_log.warning("{}".format(format_exc())) return True except Exception as e: from traceback import format_exc self.app_log.warning("{}".format(format_exc()))
async def sync_bottom_up(self): #bottom up syncing last_latest = self.latest_block self.latest_block = self.config.LatestBlock.block if last_latest: if self.latest_block.index > last_latest.index: self.app_log.info( 'Block height: %s | time: %s' % (self.latest_block.index, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))) self.config.health.consensus.last_activity = time() latest_consensus = await self.mongo.async_db.consensus.find_one({ 'index': self.latest_block.index + 1, 'block.version': CHAIN.get_version_for_height(self.latest_block.index + 1), 'ignore': { '$ne': True } }) if latest_consensus: self.remove_pending_transactions_now_in_chain(latest_consensus) self.remove_fastgraph_transactions_now_in_chain(latest_consensus) latest_consensus = await Block.from_dict(latest_consensus['block']) if self.debug: self.app_log.info("Latest consensus_block {}".format( latest_consensus.index)) records = await self.mongo.async_db.consensus.find({ 'index': self.latest_block.index + 1, 'block.version': CHAIN.get_version_for_height(self.latest_block.index + 1), 'ignore': { '$ne': True } }).to_list(length=100) for record in sorted(records, key=lambda x: int(x['block']['target'], 16)): try: block = await Block.from_dict(record['block']) except: continue stream = await self.config.peer.get_peer_by_id( record['peer']['rid']) if stream and hasattr(stream, 'peer') and stream.peer.authenticated: await self.block_queue.add( ProcessingQueueItem(await Blockchain.init_async(block), stream)) return True else: # this path is for syncing only. # Stack: # search_network_for_new # request_blocks # getblocks <--- rpc request # blocksresponse <--- rpc response # process_block_queue return await self.search_network_for_new()
async def test_block(self, block): try: block.verify() except Exception as e: self.config.app_log.warning("Integrate block error 1: {}".format(e)) return False async def get_txns(txns): for x in txns: yield x async def get_inputs(inputs): for x in inputs: yield x if block.index == 0: return True last_block = await Block.from_dict(await self.config.mongo.async_db.blocks.find_one({'index': block.index - 1})) if block.index >= CHAIN.FORK_10_MIN_BLOCK: target = await CHAIN.get_target_10min(block.index, last_block, block) else: target = await CHAIN.get_target(block.index, last_block, block) delta_t = int(time()) - int(last_block.time) special_target = CHAIN.special_target(block.index, block.target, delta_t, get_config().network) if block.index >= 35200 and delta_t < 600 and block.special_min: return False used_inputs = {} i = 0 async for transaction in get_txns(block.transactions): self.config.app_log.warning('verifying txn: {} block: {}'.format(i, block.index)) i += 1 try: await transaction.verify() except InvalidTransactionException as e: self.config.app_log.warning(e) return False except InvalidTransactionSignatureException as e: self.config.app_log.warning(e) return False except MissingInputTransactionException as e: self.config.app_log.warning(e) except NotEnoughMoneyException as e: self.config.app_log.warning(e) return False except Exception as e: self.config.app_log.warning(e) return False if transaction.inputs: failed = False used_ids_in_this_txn = [] async for x in get_inputs(transaction.inputs): if self.config.BU.is_input_spent(x.id, transaction.public_key, from_index=block.index): failed = True if x.id in used_ids_in_this_txn: failed = True if (x.id, transaction.public_key) in used_inputs: failed = True used_inputs[(x.id, transaction.public_key)] = transaction used_ids_in_this_txn.append(x.id) if failed and block.index >= CHAIN.CHECK_DOUBLE_SPEND_FROM: return False elif failed and block.index < CHAIN.CHECK_DOUBLE_SPEND_FROM: continue if block.index >= 35200 and delta_t < 600 and block.special_min: self.config.app_log.warning('1') return False if int(block.index) > CHAIN.CHECK_TIME_FROM and int(block.time) < int(last_block.time): self.config.app_log.warning('2') return False if last_block.index != (block.index - 1) or last_block.hash != block.prev_hash: self.config.app_log.warning('3') return False if int(block.index) > CHAIN.CHECK_TIME_FROM and (int(block.time) < (int(last_block.time) + 600)) and block.special_min: self.config.app_log.warning('4') return False if block.index >= 35200 and delta_t < 600 and block.special_min: self.config.app_log.warning('5') return False target_block_time = CHAIN.target_block_time(self.config.network) checks_passed = False if (int(block.hash, 16) < target): self.config.app_log.warning('6') checks_passed = True elif (block.special_min and int(block.hash, 16) < special_target): self.config.app_log.warning('7') checks_passed = True elif (block.special_min and block.index < 35200): self.config.app_log.warning('8') checks_passed = True elif (block.index >= 35200 and block.index < 38600 and block.special_min and (int(block.time) - int(last_block.time)) > target_block_time): self.config.app_log.warning('9') checks_passed = True else: self.config.app_log.warning("Integrate block error - index and time error") if not checks_passed: return False return True