예제 #1
0
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)
예제 #2
0
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
예제 #3
0
    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)
예제 #5
0
    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)
예제 #6
0
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)
예제 #7
0
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
예제 #8
0
 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
예제 #9
0
    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)
예제 #10
0
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)
예제 #11
0
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
예제 #12
0
 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
예제 #13
0
    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)
예제 #14
0
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)
예제 #15
0
 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
예제 #16
0
    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)))
예제 #17
0
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)
예제 #18
0
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)
예제 #19
0
 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
예제 #20
0
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)