async def request_blocks(self, hash_start: types.UInt256, count: int = None) -> None: """ Send a request for retrieving block hashes from `hash_start` to `hash_start`+`count`. Not specifying a `count` results in requesting at most 500 blocks. Note: The remote node is expected to reply with a Message with the :const:`~neo3.network.message.MessageType.INV` type containing the hashes of the requested blocks. Use :meth:`~neo3.network.node.NeoNode.request_data` in combination with these hashes to return the actual :class:`~neo3.network.payloads.block.Block` objects. See also: :meth:`~neo3.network.node.NeoNode.request_block_data()` to immediately retrieve :class:`~neo3.network.payloads.block.Block` objects. Args: hash_start: count: """ m = message.Message(msg_type=message.MessageType.GETBLOCKS, payload=payloads.GetBlocksPayload( hash_start, count)) await self.send_message(m)
async def send_address_list( self, network_addresses: List[payloads.NetworkAddress]) -> None: """ Send network addresses. Args: network_addresses: """ m = message.Message( msg_type=message.MessageType.ADDR, payload=payloads.AddrPayload(addresses=network_addresses)) await self.send_message(m)
async def _do_handshake( self) -> Tuple[bool, Optional[payloads.DisconnectReason]]: caps: List[capabilities.NodeCapability] = [ capabilities.FullNodeCapability(0) ] # TODO: fix nonce and port if a service is running send_version = message.Message(msg_type=message.MessageType.VERSION, payload=payloads.VersionPayload.create( nonce=123, user_agent="NEO3-PYTHON", capabilities=caps)) await self.send_message(send_version) m = await self.read_message(timeout=3) if not m or m.type != message.MessageType.VERSION: await self.disconnect( payloads.DisconnectReason.HANDSHAKE_VERSION_ERROR) return (False, payloads.DisconnectReason.HANDSHAKE_VERSION_ERROR) if not self._validate_version(m.payload): await self.disconnect( payloads.DisconnectReason.HANDSHAKE_VERSION_ERROR) return (False, payloads.DisconnectReason.HANDSHAKE_VERSION_ERROR) m_verack = message.Message(msg_type=message.MessageType.VERACK) await self.send_message(m_verack) m = await self.read_message(timeout=3) if not m or m.type != message.MessageType.VERACK: await self.disconnect( payloads.DisconnectReason.HANDSHAKE_VERACK_ERROR) return (False, payloads.DisconnectReason.HANDSHAKE_VERACK_ERROR) logger.debug( f"Connected to {self.version.user_agent} @ {self.address.address}: {self.best_height}." ) msgrouter.on_node_connected(self) return (True, None)
async def send_headers(self, headers: List[payloads.Header]) -> None: """ Send a list of Header objects. Args: headers: """ if len(headers) > 2000: headers = headers[:2000] m = message.Message(msg_type=message.MessageType.HEADERS, payload=payloads.HeadersPayload(headers)) await self.send_message(m)
def handler_ping(self, msg: message.Message) -> None: """ Handler for a message with the PING type. Args: msg: """ if settings.database: height = max(0, blockchain.Blockchain().height) else: height = 0 m = message.Message(msg_type=message.MessageType.PONG, payload=payloads.PingPayload(height=height)) self._create_task_with_cleanup(self.send_message(m))
async def request_data(self, type: payloads.InventoryType, hashes: List[types.UInt256]) -> None: """ Send a request for receiving the specified inventory data. Args: type: hashes: """ if len(hashes) < 1: return m = message.Message(msg_type=message.MessageType.GETDATA, payload=payloads.InventoryPayload(type, hashes)) await self.send_message(m)
async def request_headers(self, hash_start: types.UInt256, count: int = None) -> None: """ Send a request for headers from `hash_start` to `hash_start`+`count`. Not specifying a `count` results in requesting at most 2000 headers. Args: hash_start: count: """ m = message.Message(msg_type=message.MessageType.GETHEADERS, payload=payloads.GetBlocksPayload( hash_start, count)) await self.send_message(m)
async def _monitor_node_height(self) -> None: now = datetime.utcnow().timestamp() for node in self.nodes: if now - node.best_height_last_update > self.MAX_HEIGHT_UPDATE_DURATION: logger.debug(f"Disconnecting node {node.nodeid} Reason: max height update threshold exceeded.") asyncio.create_task(node.disconnect(reason=payloads.DisconnectReason.POOR_PERFORMANCE)) else: logger.debug(f"Asking node {node.nodeid_human} to send us a height update (PING)") # Request latest height from node if settings.database: height = max(0, blockchain.Blockchain().height) else: height = 0 m = message.Message(msg_type=message.MessageType.PING, payload=payloads.PingPayload(height=height)) task = asyncio.create_task(node.send_message(m)) self.tasks.append(task) task.add_done_callback(lambda fut: self.tasks.remove(fut))
async def request_block_data(self, index_start, count) -> None: """ Send a request for `count` blocks starting from `index_start`. Count cannot exceed :attr:`~neo3.network.payloads.block.GetBlockDataPayload.MAX_BLOCKS_COUNT`. See also: :meth:`~neo3.network.node.NeoNode.request_blocks()` to only request block hashes. Args: index_start: block index to start from. count: number of blocks to return. """ m = message.Message(msg_type=message.MessageType.GETBLOCKDATA, payload=payloads.GetBlockDataPayload( index_start, count)) await self.send_message(m)
def handler_inv(self, msg: message.Message) -> None: """ Handler for a message with the INV type. Args: msg: """ payload = cast(payloads.InventoryPayload, msg.payload) if payload.type == payloads.InventoryType.BLOCK: # neo-cli broadcasts INV messages on a regular interval. We can use those as trigger to request # their latest block height if len(payload.hashes) > 0: if settings.database: height = max(0, blockchain.Blockchain().height) else: height = 0 m = message.Message( msg_type=message.MessageType.PING, payload=payloads.PingPayload(height=height)) self._create_task_with_cleanup(self.send_message(m)) else: logger.debug( f"Message with type INV received. No processing for payload type " # type:ignore f"{payload.type.name} implemented")
async def send_inventory(self, inv_type: payloads.InventoryType, inv_hash: types.UInt256): inv = payloads.InventoryPayload(type=inv_type, hashes=[inv_hash]) m = message.Message(msg_type=message.MessageType.INV, payload=inv) await self.send_message(m)
async def request_address_list(self) -> None: """ Send a request for receiving known network addresses. """ m = message.Message(msg_type=message.MessageType.GETADDR) await self.send_message(m)