def connect_request_handler(msg, gossiper): """Handles connection request events. When a connection request message arrives, the requesting node is added as a peer and a reply message is sent. Args: msg (Message): The received connection request message. gossiper (Node): The local node. """ if msg.SenderID != msg.OriginatorID: logger.error('connection request must originate from peer; %s not %s', msg.OriginatorID, msg.SenderID) return name = msg.Name if not name: name = msg.OriginatorID[:8] orignode = node.Node(address=msg.NetAddress, identifier=msg.OriginatorID, name=name) orignode.Enabled = True gossiper.add_node(orignode) reply = ConnectReplyMessage() reply.InReplyTo = msg.Identifier gossiper.send_message(reply, msg.OriginatorID)
def topology_reply_handler(msg, gossiper): """Handles incoming topology reply messages. Args: msg (message.Message): The incoming topology message. gossiper (Node): The local node. """ logger.debug('received reply to probe %s from node %s', msg.InReplyTo[:8], msg.Name) global CurrentTopologyRequestID, CurrentTopologyResponseMap # Because of the multiple paths through the overlay network, the topology # request can arrive after replies have started to arrive so we initialize # the current set of requests with the replies that come in if not CurrentTopologyRequestID: CurrentTopologyRequestID = msg.InReplyTo reactor.callLater(TimeToWaitForTopologyProbe, update_peers_from_topology_probe, gossiper, None) if msg.InReplyTo != CurrentTopologyRequestID: logger.debug('reply for a different probe, %s instead of %s', msg.InReplyTo[:8], CurrentTopologyRequestID[:8]) return peer = node.Node(address=msg.NetAddress, identifier=msg.NodeIdentifier, name=msg.Name) CurrentTopologyResponseMap[peer] = msg.Peers
def __init__(self, baseurl, creator=None, name='txnclient', keystring=None, keyfile=None, state=None, tokenstore=None): super(MarketPlaceClient, self).__init__(baseurl) self.CreatorID = creator self.LastTransaction = None self.CurrentState = state or MarketPlaceState(self.BaseURL) self.TokenStore = tokenstore # set up the signing key if keystring: logger.debug("set signing key from string\n%s", keystring) signingkey = signed_object.generate_signing_key(wifstr=keystring) elif keyfile: logger.debug("set signing key from file %s", keyfile) signingkey = signed_object.generate_signing_key( wifstr=open(keyfile, "r").read()) else: raise TypeError('expecting valid signing key, none provided') identifier = signed_object.generate_identifier(signingkey) self.LocalNode = node.Node(identifier=identifier, signingkey=signingkey, name=name)
def __init__(self, baseurl, name='IntegerKeyClient', keystring=None, keyfile=None, state=None): super(IntegerKeyClient, self).__init__(baseurl) self.LastTransaction = None self.CurrentState = state or IntegerKeyState(self.BaseURL) self.CurrentState.fetch() # set up the signing key if keystring: logger.debug("set signing key from string\n%s", keystring) signingkey = signed_object.generate_signing_key(wifstr=keystring) elif keyfile: logger.debug("set signing key from file %s", keyfile) signingkey = signed_object.generate_signing_key( wifstr=open(keyfile, "r").read()) else: raise TypeError('expecting valid signing key, none provided') identifier = signed_object.generate_identifier(signingkey) self.LocalNode = node.Node(identifier=identifier, signingkey=signingkey, name=name)
def initialize_ledger_object(self): # Create the local ledger instance name = self.Config['NodeName'] if name in self.NodeMap: nd = self.NodeMap[name] else: host = self.Config['Host'] port = self.Config['Port'] addr = (socket.gethostbyname(host), port) signingkey = signed_object.generate_signing_key( wifstr=self.Config.get('SigningKey')) identifier = signed_object.generate_identifier(signingkey) nd = node.Node(address=addr, identifier=identifier, signingkey=signingkey, name=name) self.initialize_ledger_from_node(nd) assert self.Ledger for txnfamily in self.DefaultTransactionFamilies: txnfamily.register_transaction_types(self.Ledger) self.Ledger.onNodeDisconnect += self.handle_node_disconnect_event logger.info("starting ledger %s with id %s at network address %s", self.Ledger.LocalNode, self.Ledger.LocalNode.Identifier[:8], self.Ledger.LocalNode.NetAddress)
def connect_syn_handler(msg, gossiper): """Handles connection syn events. When a connection syn message arrives, the requesting node is added as a node and an ack message is sent. We do not yet know whether the requester can hear us, so we'll not set the node to a peer just yet. Args: msg (message.Message): The received connection request message. gossiper (Node): The local node. """ if msg.OriginatorID in gossiper.blacklist: logger.warning('msg originator %s blacklisted', msg.OriginatorID) return if msg.SenderID != msg.OriginatorID: logger.error('connection request must originate from peer; %s not %s', msg.OriginatorID, msg.SenderID) return name = msg.Name if not name: name = msg.OriginatorID[:8] orignode = node.Node(address=msg.NetAddress, identifier=msg.OriginatorID, name=name) gossiper.add_node(orignode) reply = ConnectAckMessage() reply.InReplyTo = msg.Identifier gossiper.send_message(reply, msg.OriginatorID)
def send_to_closest_nodes(ledger, msg, addr, count=1, initialize=True): """ Send a message to a set of "close" nodes where close is defined by the distance metric in EndpointRegistryStore (XOR distance). If the nodes are not currently in the ledger's node map, then add them. """ storemap = ledger.GlobalStore epstore = storemap.GetTransactionStore( EndpointRegistryTransaction.TransactionTypeName) assert epstore nodes = epstore.FindClosestNodes(addr, count) if ledger.LocalNode.Identifier in nodes: nodes.remove(ledger.LocalNode.Identifier) if nodes: # make sure the nodes are all in the node map for nodeid in nodes: if nodeid not in ledger.NodeMap: ninfo = epstore[nodeid] addr = (ninfo['Host'], int(ninfo['Port'])) ledger.AddNode( node.Node(address=addr, identifier=nodeid, name=ninfo['Name'])) logger.debug('send %s to %s', str(msg), ",".join(nodes)) ledger.MulticastMessage(msg, nodes) return nodes
def initialize_node_map(self): self.NodeMap = {} for nodedata in self.config.get("Nodes", []): addr = (socket.gethostbyname(nodedata["Host"]), nodedata["Port"]) nd = node.Node(address=addr, identifier=nodedata["Identifier"], name=nodedata["NodeName"]) self.NodeMap[nodedata["NodeName"]] = nd
def __init__(self, base_url, store_name=None, name='SawtoothClient', transaction_type=None, message_type=None, keystring=None, keyfile=None, disable_client_validation=False): self._base_url = base_url self._message_type = message_type self._transaction_type = transaction_type # An explicit store name takes precedence over a store name # implied by the transaction type. self._store_name = None if store_name is not None: self._store_name = store_name.strip('/') elif transaction_type is not None: self._store_name = transaction_type.TransactionTypeName.strip('/') self._communication = _Communication(base_url) self._last_transaction = None self._local_node = None self._update_batch = None self._disable_client_validation = disable_client_validation # We only keep current state if we have a store name self._current_state = None if self._store_name is not None: state_type = global_store_manager.KeyValueStore if transaction_type is not None: state_type = transaction_type.TransactionStoreType self._current_state = \ _ClientState(client=self, state_type=state_type) self.fetch_state() signing_key = None if keystring: LOGGER.debug("set signing key from string\n%s", keystring) signing_key = signed_object.generate_signing_key(wifstr=keystring) elif keyfile: LOGGER.debug("set signing key from file %s", keyfile) try: signing_key = signed_object.generate_signing_key( wifstr=open(keyfile, "r").read().strip()) except IOError as ex: raise ClientException("Failed to load key file: {}".format( str(ex))) if signing_key: identifier = signed_object.generate_identifier(signing_key) self._local_node = node.Node(identifier=identifier, signingkey=signing_key, name=name)
def random_walk_handler(msg, gossiper): """Function called when the gossiper receives a RandomWalkMessage from one of its peers. Args: msg (message.Message): The received random walk message. gossiper (Node): The local node. """ if msg.OriginatorID == gossiper.LocalNode.Identifier: logger.debug('node %s received its own random walk request, ignore', gossiper.LocalNode) return logger.debug('random walk request %s from %s with ttl %d', msg.Identifier[:8], msg.Name, msg.TimeToLive) peers = gossiper.peer_id_list() # if the source is not already one of our peers, then check to see if we # should add it to our list if msg.OriginatorID not in peers: if len(peers) < random_connections(): logger.debug( 'add connection to node %s based on random walk request %s', msg.Name, msg.Identifier[:8]) onode = node.Node(address=msg.NetAddress, identifier=msg.NodeIdentifier, name=msg.Name) onode.enable() send_connection_request(gossiper, onode) return # if there is still life in the message, then see if we should forward it # to another node if msg.TimeToLive > 0: # see if we can find a peer other than the peer who forwarded the # message to us, if not then we'll just drop the request try: peers.remove(msg.SenderID) peers.remove(msg.OriginatorID) except: pass if len(peers) > 0: peerid = random.choice(peers) gossiper.send_message(msg, peerid, initialize=False)
def node_from_config(config): name = config['NodeName'] signing_key = generate_signing_key(wifstr=read_key_file(config['KeyFile'])) listen_info = config.get("Listen") (gossip_host, gossip_port) = parse_listen_directives(listen_info)['gossip'] # stubbing endpoint address for now endpoint_addr = (None, None) nd = node.Node( address=(socket.gethostbyname(gossip_host), gossip_port), identifier=generate_identifier(signing_key), signingkey=signing_key, name=name, endpoint_address=endpoint_addr, ) return nd
def initialize_quorum_map(self, quorum, nodes): q = quorum if self.local_node.Name not in q: logger.fatal("node must be in its own quorum") self.shutdown() return if len(q) < self.VotingQuorumTargetSize: logger.fatal('insufficient quorum configuration; need %s but " \ "specified %s', len(q), self.VotingQuorumTargetSize) self.shutdown() return self.QuorumMap = {} for nd_dict in nodes: if nd_dict["NodeName"] in q: addr = (socket.gethostbyname(nd_dict["Host"]), nd_dict["Port"]) nd = node.Node(address=addr, identifier=nd_dict["Identifier"], name=nd_dict["NodeName"]) nd.HttpPort = nd_dict["HttpPort"] self.QuorumMap[nd_dict["NodeName"]] = nd
def __init__(self, base_url, store_name, name='SawtoothClient', transaction_type=None, message_type=None, keystring=None, keyfile=None): self._transaction_type = transaction_type self._message_type = message_type self._base_url = base_url self._communication = _Communication(base_url) self._last_transaction = None self._local_node = None state_communication = self._communication if base_url else None self._current_state = _ClientState( communication=state_communication, store_name=store_name, state_type=transaction_type.TransactionStoreType if transaction_type else global_store_manager.KeyValueStore) self._update_batch = None self.fetch_state() signing_key = None if keystring: LOGGER.debug("set signing key from string\n%s", keystring) signing_key = signed_object.generate_signing_key(wifstr=keystring) elif keyfile: LOGGER.debug("set signing key from file %s", keyfile) try: signing_key = signed_object.generate_signing_key( wifstr=open(keyfile, "r").read().strip()) except IOError as ex: raise ClientException("Failed to load key file: {}".format( str(ex))) if signing_key: identifier = signed_object.generate_identifier(signing_key) self._local_node = node.Node(identifier=identifier, signingkey=signing_key, name=name)
def quorum_advertisement_handler(msg, journal): """Function called when the journal receives a QuorumAdvertisementMessage from one of its peers. Args: msg (QuorumAdvertisementMessage): The received quorum advertisement message. journal (QuorumJournal): The journal which received the message. """ logger.info("quorum advertisement received from %s", msg.OriginatorID[:8]) if msg.OriginatorID == journal.LocalNode.Identifier: logger.debug( 'node %s received its own quorum advertisement request, ignore', journal.LocalNode) return onode = node.Node(address=msg.NetAddress, identifier=msg.Identifier, name=msg.Name) journal.add_quorum_node(onode) # if there is still life in the message, then see if we should forward it # to another node if msg.TimeToLive > 0: # see if we can find a peer other than the peer who forwarded the # message to us, if not then we'll just drop the request try: peers = journal.peer_id_list() peers.remove(msg.SenderID) peers.remove(msg.OriginatorID) except: pass if len(peers) > 0: peerid = random.choice(peers) journal.send_message(msg, peerid, initialize=False)
def initialize_quorum_map(self, config): q = config.get('Quorum', []) if self.LocalNode.Name not in q: logger.fatal("node must be in its own quorum") self.shutdown() return if len(q) < config.get("VotingQuorumTargetSize"): logger.fatal( 'insufficient quorum configuration; need %s but " \ "specified %s', len(q), config.get('VotingQuorumTargetSize', None)) self.shutdown() return self.QuorumMap = {} for nd_dict in config.get("Nodes", []): if nd_dict["NodeName"] in q: addr = (socket.gethostbyname(nd_dict["Host"]), nd_dict["Port"]) nd = node.Node(address=addr, identifier=nd_dict["Identifier"], name=nd_dict["NodeName"]) nd.HttpPort = nd_dict["HttpPort"] self.QuorumMap[nd_dict["NodeName"]] = nd
def get_endpoints(self, count, domain='/'): endpoints = [] eplist = self.LedgerWebClient.get_store( endpoint_registry.EndpointRegistryTransaction) if not eplist: return endpoints for ep in eplist: epinfo = self.LedgerWebClient.get_store( endpoint_registry.EndpointRegistryTransaction, ep) if epinfo.get('Domain', '/').startswith(domain): addr = (socket.gethostbyname(epinfo["Host"]), epinfo["Port"]) endpoint = node.Node(address=addr, identifier=epinfo["NodeIdentifier"], name=epinfo["Name"]) endpoints.append(endpoint) logger.info('found %d endpoints', len(endpoints)) if count <= 0: return endpoints return random.sample(endpoints, min(count, len(endpoints)))
def _prepare_genesis_transactions(journal): logger.debug('prepare marketplace transactions for genesis block') # Set up a random key for generating the genesis objects, we # "trust" that the key is not kept. At some point this should be # revisited, suggestions about "burning" the private key signingkey = signed_object.generate_signing_key() identifier = signed_object.generate_identifier(signingkey) signnode = node.Node(identifier=identifier, signingkey=signingkey) # Create a participant for the global market txn = MarketPlaceTransaction( minfo={ 'Updates': [{ 'UpdateType': participant_update.Register.UpdateType, 'Name': 'marketplace', 'Description': 'The ROOT participant for the marketplace', }] }) txn.sign_from_node(signnode) journal.add_pending_transaction(txn) logger.info('Created market participant: %s', txn.Identifier) lasttxn = txn.Identifier mktid = txn.Identifier # Create an asset type for participants txn = MarketPlaceTransaction( minfo={ 'Updates': [{ 'UpdateType': asset_type_update.Register.UpdateType, 'CreatorId': mktid, 'Restricted': False, 'Name': '/asset-type/participant', 'Description': 'Canonical type for participant assets' }] }) txn.Dependencies = [lasttxn] txn.sign_from_node(signnode) journal.add_pending_transaction(txn) logger.info('Created participant asset type: %s', txn.Identifier) lasttxn = txn.Identifier # Create an asset type for random tokens txn = MarketPlaceTransaction( minfo={ 'Updates': [{ 'UpdateType': asset_type_update.Register.UpdateType, 'CreatorId': mktid, 'Restricted': True, 'Name': '/asset-type/token', 'Description': 'Canonical type for meaningless tokens that ' 'are useful for bootstrapping' }] }) txn.Dependencies = [lasttxn] txn.sign_from_node(signnode) journal.add_pending_transaction(txn) logger.info('Created token asset type: %s', txn.Identifier) lasttxn = txn.Identifier assettypeid = txn.Identifier # Create an asset for the tokens txn = MarketPlaceTransaction( minfo={ 'Updates': [{ 'UpdateType': asset_update.Register.UpdateType, 'CreatorId': mktid, 'AssetTypeId': assettypeid, 'Restricted': False, 'Consumable': False, 'Divisible': False, 'Name': '/asset/token', 'Description': 'Canonical asset for tokens' }] }) txn.Dependencies = [lasttxn] txn.sign_from_node(signnode) journal.add_pending_transaction(txn) logger.info('Created token asset: %s', txn.Identifier) # Create an asset for the validation tokens txn = MarketPlaceTransaction( minfo={ 'Updates': [{ 'UpdateType': asset_update.Register.UpdateType, 'CreatorId': mktid, 'AssetTypeId': assettypeid, 'Restricted': True, 'Consumable': True, 'Divisible': False, 'Name': '/asset/validation-token', 'Description': 'Canonical asset for validation tokens' }] }) txn.Dependencies = [lasttxn] txn.sign_from_node(signnode) journal.add_pending_transaction(txn) logger.info('Created validation token asset: %s', txn.Identifier)
def _prepare_genesis_transactions(journal): logger.debug('prepare marketplace transactions for genesis block') # Set up a random key for generating the genesis objects, we # "trust" that the key is not kept. At some point this should be # revisited, suggestions about "burning" the private key signingkey = signed_object.generate_signing_key() identifier = signed_object.generate_identifier(signingkey) signnode = node.Node(identifier=identifier, signingkey=signingkey) # Create a participant for the global market txn = MarketPlaceTransaction() update = participant_update.Register(txn) update.Name = 'marketplace' update.Description = 'The ROOT participant for the marketplace' txn.Update = update txn.sign_from_node(signnode) journal.add_pending_transaction(txn) logger.info('Created market participant: %s', txn.Identifier) lasttxn = txn.Identifier mktid = update.ObjectID # Create an asset type for participants txn = MarketPlaceTransaction() update = asset_type_update.Register(txn) update.CreatorID = mktid # anyone can create participant assets for themselves update.Restricted = False update.Name = '/asset-type/participant' update.Description = 'Canonical type for participant assets' txn.Update = update txn.Dependencies = [lasttxn] txn.sign_from_node(signnode) journal.add_pending_transaction(txn) logger.info('Created participant asset type: %s', txn.Identifier) lasttxn = txn.Identifier assettypeid = update.ObjectID # Create an asset type for random tokens txn = MarketPlaceTransaction() update = asset_type_update.Register(txn) update.CreatorID = mktid update.Restricted = True # there is only one asset based on the token type update.Name = '/asset-type/token' update.Description = 'Canonical type for meaningless tokens that are ' \ 'very useful for bootstrapping' txn.Update = update txn.Dependencies = [lasttxn] txn.sign_from_node(signnode) journal.add_pending_transaction(txn) logger.info('Created token asset type: %s', txn.Identifier) lasttxn = txn.Identifier assettypeid = update.ObjectID # Create an asset for the tokens txn = MarketPlaceTransaction() update = asset_update.Register(txn) update.CreatorID = mktid update.AssetTypeID = assettypeid # anyone can create holdings with token instances update.Restricted = False # and these are infinitely replaceable update.Consumable = False update.Divisible = False update.Name = '/asset/token' update.Description = 'Canonical asset for tokens' txn.Update = update txn.Dependencies = [lasttxn] txn.sign_from_node(signnode) journal.add_pending_transaction(txn) logger.info('Created token asset: %s', txn.Identifier) # Create an asset for the validation tokens txn = MarketPlaceTransaction() update = asset_update.Register(txn) update.CreatorID = mktid update.AssetTypeID = assettypeid update.Restricted = True # these assets are only created by the market update.Consumable = True update.Divisible = False update.Name = '/asset/validation-token' update.Description = 'Canonical asset for validation tokens' txn.Update = update txn.Dependencies = [lasttxn] txn.sign_from_node(signnode) journal.add_pending_transaction(txn) logger.info('Created validation token asset: %s', txn.Identifier)
def _endpoint_info_to_node(epinfo): addr = (socket.gethostbyname(epinfo["Host"]), epinfo["Port"]) nd = node.Node(address=addr, identifier=epinfo["NodeIdentifier"], name=epinfo["Name"]) return nd
def parse_networking_info(config): ''' Provides a DRY location for parsing a validator's intended network interface specifications from that validator's configuration Args: config: (dict) - fully resolved configuration dictionary (c.f. sawtooth.Config) Returns: (nd, http_port): an ordered pair, where: nd: (gossip.Node) http_port: (int) or (None) ''' # Parse the listen directives from the configuration so # we know what to bind gossip protocol to listen_info = config.get("Listen") listen_directives = parse_listen_directives(listen_info) # If the gossip listen address is 0.0.0.0, then there must be # an Endpoint.Host entry as we don't know what to put in the # endpoint registry otherwise. if listen_directives['gossip'].host == '0.0.0.0' and \ ('Endpoint' not in config or 'Port' not in config['Endpoint']): raise Exception('gossip listen address is 0.0.0.0, but endpoint host ' 'missing from configuration') gossip_host = listen_directives['gossip'].host gossip_port = listen_directives['gossip'].port # The endpoint host/port and HTTP port come from the listen data, but # can be overridden by the configuration. endpoint_host = gossip_host endpoint_port = gossip_port endpoint_http_port = None if 'http' in listen_directives: endpoint_http_port = listen_directives['http'].port # See if we need to override the endpoint data endpoint_cfg = config.get('Endpoint', None) if endpoint_cfg is not None: if 'Host' in endpoint_cfg: endpoint_host = endpoint_cfg['Host'] if 'Port' in endpoint_cfg: endpoint_port = int(endpoint_cfg['Port']) if 'HttpPort' in endpoint_cfg: endpoint_http_port = int(endpoint_cfg['HttpPort']) # Finally, if the endpoint host is 'localhost', we need to convert it # because to another host, obviously 'localhost' won't mean "us" if endpoint_host == 'localhost': endpoint_host = socket.gethostbyname(endpoint_host) name = config['NodeName'] addr = (socket.gethostbyname(gossip_host), gossip_port) endpoint_addr = (endpoint_host, endpoint_port) signingkey = signed_object.generate_signing_key( wifstr=config.get('SigningKey')) identifier = signed_object.generate_identifier(signingkey) nd = node.Node(address=addr, identifier=identifier, signingkey=signingkey, name=name, endpoint_address=endpoint_addr) return (nd, endpoint_http_port)