node
— NEO network node¶
A NeoNode
in the networking context is considered an P2P endpoint with which NEO specific network messages can be exchanged. It is not to be confused with the generic node term commonly used to refer to a client application that participates in the blockchain network. A node in the generic term would consist of a NeoNode for the network layer, a virtual machine for transaction processing, a persistence layer for storing data and optionally an interface like a GUI or CLI.
Connecting
Establishing a connection to a node requires just 3 pieces of information.
a network
magic
to select the right network (MainNet, TestNet, private net).an IP address.
a port number.
With this information at hand we can establish a basic connection as follows.
import asyncio
from neo3 import settings
from neo3.network import node
async def main():
# set network magic to NEO MainNet
settings.network.magic = 5195086
node_client, error = await node.NeoNode.connect_to('127.0.0.1', 40333)
if node_client:
print(f"Connected to {node_client.version.user_agent} @ {node_client.address}")
# send and receive messages
if __name__ == "__main__":
asyncio.run(main())
The connect_to()
function establishes a network connection and performs the initial handshake. Under the hood it uses asyncio.loop.create_connection
with a NeoProtocol
as the factory.
Once a connection is established it is up to the caller to implement a message loop for handling incoming messages. Such a loop can be started via the convenience start_message_handler()
method. This loop reads network messages and dispatches them to their associates handlers.
Connect and disconnect events are broadcasted using Events via on_node_connected(client_instance) and on_node_disconnected(client_instance, reason). You can listen to these events as follows:
from neo3.core import msgrouter
def node_connected(node_client):
print(f"Connected to node {node_client.version.user_agent} @ {node_client.address}")
def node_disconnected(node_client, reason):
print(f"Disconnected from node {node_client.version.user_agent} @ {node_client.address} for reason {reason}")
msgrouter.on_node_connected += node_connected
msgrouter.on_node_disconnected += node_disconnected
When connected you likely want to exchange data.
Exchanging data
At the most basic level you have the send_message()
and read_message()
commands. These respectivily take or provide you a network Message and a payload.
A handfull of convenience functions are present for common requests and responses. Specifically:
Finally, in conjunction with the convenience message loop (started via start_message_handler()
) a list of message handlers are available that can be overwritten by updating the dispatch_table
dictionary to set your own handler. All handlers start with handler_<name> and take a Message
as parameter.
-
class
neo3.network.node.
NeoNode
(protocol)¶ -
async static
connect_to
(host=None, port=None, timeout=3, loop=None, socket=None)¶ Establish a connection to a Neo node
Note: performs the initial connection handshake and validation.
- Parameters
- Raises
ValueError – if host/port and the socket argument as specified as the same time or none are specified.
- Returns
(Node instance, None) - if a connection was successfully established
(None, (ip address, error reason)) - if a connection failed to establish . Reasons include connection timeout, connection full and handshake errors. # noqa
- Return type
Tuple
-
async
connection_lost
(exc)¶ Event called by the
base protocol
.- Return type
-
async
connection_made
(transport)¶ Event called by the
base protocol
.- Return type
-
async
disconnect
(reason)¶ Close the connection to remote endpoint.
- Parameters
reason (
DisconnectReason
) – reason for disconnecting.- Return type
-
classmethod
get_address_new
()¶ Utility function to return the first address with the state NEW.
- Return type
Optional
[NetworkAddress
]
-
handler_addr
(msg)¶ Handler for a message with the ADDR type.
-
handler_block
(msg)¶ Handler for a message with the BLOCK type.
-
handler_consensus
(msg)¶ Handler for a message with the CONSENSUS type.
-
handler_filteradd
(msg)¶ Handler for a message with the FILTERADD type.
-
handler_filterclear
(msg)¶ Handler for a message with the FILTERCLEAR type.
-
handler_filterload
(msg)¶ Handler for a message with the FILTERLOAD type.
-
handler_getaddr
(msg)¶ Handler for a message with the GETADDR type.
-
handler_getblockdata
(msg)¶ Handler for a message with the GETBLOCKBYINDEX type.
-
handler_getblocks
(msg)¶ Handler for a message with the GETBLOCKS type.
-
handler_getdata
(msg)¶ Handler for a message with the GETDATA type.
-
handler_getheaders
(msg)¶ Handler for a message with the GETHEADERS type.
-
handler_headers
(msg)¶ Handler for a message with the HEADERS type.
-
handler_inv
(msg)¶ Handler for a message with the INV type.
-
handler_mempool
(msg)¶ Handler for a message with the MEMPOOL type.
-
handler_merkleblock
(msg)¶ Handler for a message with the MERKLEBLOCK type.
-
handler_ping
(msg)¶ Handler for a message with the PING type.
-
handler_pong
(msg)¶ Handler for a message with the PONG type.
-
handler_transaction
(msg)¶ Handler for a message with the TRANSACTION type.
-
async
read_message
(timeout=30)¶ Read a Message from the wire.
-
async
relay
(inventory)¶ Relay the inventory to the network
- Parameters
inventory (
IInventory
) – should be of type Block, Transaction or Consensus. See:InventoryType
. # noqa- Return type
-
async
request_address_list
()¶ Send a request for receiving known network addresses.
- Return type
-
async
request_block_data
(index_start, count)¶ Send a request for count blocks starting from index_start.
Count cannot exceed
MAX_BLOCKS_COUNT
.See also
request_blocks()
to only request block hashes.- Parameters
index_start – block index to start from.
count – number of blocks to return.
- Return type
-
async
request_blocks
(hash_start, count=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
INV
type containing the hashes of the requested blocks. Userequest_data()
in combination with these hashes to return the actualBlock
objects.See also
request_block_data()
to immediately retrieveBlock
objects.
-
async
request_data
(type, hashes)¶ Send a request for receiving the specified inventory data.
- Parameters
type (
InventoryType
) –
- Return type
-
async
request_headers
(index_start, count=2000)¶ Send a request for headers from index_start to index_start`+`count.
Not specifying a count results in requesting at most 2000 headers.
-
async
send_address_list
(network_addresses)¶ Send network addresses.
-
async
send_headers
(headers)¶ Send a list of Header objects.
-
async
send_inventory
(inv_type, inv_hash)¶ Send an inventory to the network
- Parameters
inv_type (
InventoryType
) –inv_hash (
UInt256
) –
- Return type
-
async
send_message
(message)¶ Send a Message over the wire.
-
start_message_handler
()¶ A convenience function to start a message reading loop and forward the messages to their respective handlers as configured in
dispatch_table
.- Return type
-
address
¶ Address of the remote endpoint.
-
addresses
: List[neo3.network.payloads.address.NetworkAddress] = []¶ a list of known network addresses (class attribute).
- Type
List[payloads.NetworkAddress]
-
disconnecting
: bool¶ Whether the node is in the process of disconnecting and shutting down its tasks.
- Type
-
dispatch_table
: Dict[neo3.network.message.MessageType, Callable[[neo3.network.message.Message], None]]¶ A table matching message types to handler functions.
- Type
Dict[message.MessageType, Callable[[message.Message], None]]
-
async static