Exemple #1
0
class ConnectionMonitor(gevent.Greenlet):

    "monitors the connection by sending pings and checking pongs"
    ping_interval = 15.
    response_delay_threshold = 120.  # FIXME, apply msg takes too long
    max_samples = 1000
    log = slogging.get_logger('p2p.ctxmonitor')

    def __init__(self, proto):
        self.log.debug('init')
        assert isinstance(proto, P2PProtocol)
        self.proto = proto
        self.samples = collections.deque(maxlen=self.max_samples)
        self.last_response = self.last_request = time.time()
        super(ConnectionMonitor, self).__init__()
        # track responses
        self.proto.receive_pong_callbacks.append(self.track_response)
        self.proto.receive_hello_callbacks.append(
            lambda p, **kargs: self.start())

    def track_response(self, proto):
        self.last_response = time.time()
        self.samples.appendleft(self.last_response - self.last_request)

    def latency(self, num_samples=max_samples):
        num_samples = min(num_samples, len(self.samples))
        return sum(
            self.samples[i]
            for i in range(num_samples)) / num_samples if num_samples else 1

    def _run(self):
        self.log.debug('started', monitor=self)
        while True:
            self.log.debug('pinging', monitor=self)
            self.proto.send_ping()
            now = self.last_request = time.time()
            gevent.sleep(self.ping_interval)
            self.log.debug('latency',
                           peer=self.proto,
                           latency='%.3f' % self.latency())
            if now - self.last_response > self.response_delay_threshold:
                self.log.debug('unresponsive_peer', monitor=self)
                self.proto.peer.report_error('not responding to ping')
                self.proto.stop()
                self.proto.peer.stop()
                self.kill()

    def stop(self):
        self.log.debug('stopped', monitor=self)
        self.kill()
Exemple #2
0
the idle bucket-refresh interval is 3600 seconds.

Aside from the previously described exclusions, node discovery closely follows system
and protocol described by Maymounkov and Mazieres.
"""
import operator
import random
import time
from functools import total_ordering

from devp2p import slogging
from .crypto import sha3
from .utils import big_endian_to_int
from rlp.utils import encode_hex, is_integer, str_to_bytes

log = slogging.get_logger('p2p.discovery.kademlia')

k_b = 8  # 8 bits per hop

k_bucket_size = 16
k_request_timeout = 3 * 300 / 1000.  # timeout of message round trips
k_idle_bucket_refresh_interval = 3600  # ping all nodes in bucket if bucket was idle
k_find_concurrency = 3  # parallel find node lookups
k_pubkey_size = 512
k_id_size = 256
k_max_node_id = 2**k_id_size - 1


def random_nodeid():
    return random.randint(0, k_max_node_id)
Exemple #3
0
import time
import re
from gevent.server import StreamServer
from gevent.socket import create_connection, timeout
from .service import WiredService
from .protocol import BaseProtocol
from .p2p_protocol import P2PProtocol
from .upnp import add_portmap, remove_portmap
from devp2p import kademlia
from .peer import Peer
from devp2p import crypto
from devp2p import utils
from rlp.utils import decode_hex

from devp2p import slogging
log = slogging.get_logger('p2p.peermgr')


def on_peer_exit(peer):
    peer.stop()


class PeerManager(WiredService):

    """
    todo:
        connects new peers if there are too few
        selects peers based on a DHT
        keeps track of peer reputation
        saves/loads peers (rather discovery buckets) to disc
Exemple #4
0
import gevent
import rlp
from rlp import sedes
from .multiplexer import Packet
from .service import WiredService
from devp2p import slogging
log = slogging.get_logger('protocol')


class ProtocolError(Exception):
    pass


class SubProtocolError(ProtocolError):
    pass


class BaseProtocol(gevent.Greenlet):
    """
    A protocol mediates between the network and the service.
    It implements a collection of commands.

    For each command X the following methods are created at initialization:
    -    packet = protocol.create_X(*args, **kargs)
    -   protocol.send_X(*args, **kargs) is a shortcut for:
            protocol.send_packet(protocol.create_X(*args, **kargs))
    - protocol._receive_X(data)


    on protocol.receive_packet, the packet is deserialized according to the command.structure
        and the command.receive method called with a dict containing the received data
 def get_logger(self, name):
     return slogging.get_logger(name)
    """
    Test scenario:
    - Run app with min_peers > max_peers to force lots of peer.stop() (too many peers)
    - After X seconds of unsuccessful (by definition) discovery check that len(peers) <= min_peers
    """
    class TestDriver(object):
        DISCOVERY_LOOP_SEC = 10
        MIN_PEERS = 2

    ExampleServiceAppDisconnect.testdriver = TestDriver()

    # To be able to run app with min_peers > max_peers one has to bypass asserts.
    app_helper.assert_config = lambda a, b, c, d: True

    app_helper.run(ExampleApp,
                   ExampleServiceAppDisconnect,
                   num_nodes=3,
                   min_peers=2,
                   max_peers=1,
                   random_port=True)


if __name__ == "__main__":
    import devp2p.slogging as slogging
    slogging.configure(config_string=':debug,p2p:info')
    log = slogging.get_logger('app')
    TestFullApp().test_inc_counter_app(3)
    TestFullApp().test_inc_counter_app(6)
    test_app_restart()
    test_disconnect()
Exemple #7
0
import gevent
import gevent.socket
import ipaddress
import rlp
from rlp.utils import decode_hex, is_integer, str_to_bytes, bytes_to_str, safe_ord
from gevent.server import DatagramServer

from devp2p import slogging
from devp2p import crypto
from devp2p import kademlia
from devp2p import utils
from .service import BaseService
from .upnp import add_portmap, remove_portmap


log = slogging.get_logger('p2p.discovery')


class DefectiveMessage(Exception):
    pass


class InvalidSignature(DefectiveMessage):
    pass


class WrongMAC(DefectiveMessage):
    pass


class PacketExpired(DefectiveMessage):
Exemple #8
0
import miniupnpc

from devp2p import slogging

log = slogging.get_logger('p2p.upnp')

_upnp = None


def _init_upnp():
    global _upnp
    if _upnp:
        return _upnp
    u = miniupnpc.UPnP()
    u.discoverdelay = 200
    try:
        log.debug('Discovering... delay=%ums' % u.discoverdelay)
        ndevices = u.discover()
        log.debug('%u device(s) detected', ndevices)
        _upnp = u
    except Exception as e:
        log.debug('Exception :%s', e)
    finally:
        return _upnp


def add_portmap(port, proto, label=''):
    u = _init_upnp()
    try:
        # select an igd
        u.selectigd()
Exemple #9
0
def main():
    # config
    import yaml
    import io
    import sys
    import signal
    import gevent
    from .peermanager import PeerManager
    from .discovery import NodeDiscovery
    from devp2p import slogging
    log = slogging.get_logger('app')

    # read config
    sample_config = b"""
discovery:
    bootstrap_nodes:
        # local bootstrap
        - enode://6ed2fecb28ff17dec8647f08aa4368b57790000e0e9b33a7b91f32c41b6ca9ba21600e9a8c44248ce63a71544388c6745fa291f88f8b81e109ba3da11f7b41b9@127.0.0.1:30303
        # go_bootstrap
        #- enode://6cdd090303f394a1cac34ecc9f7cda18127eafa2a3a06de39f6d920b0e583e062a7362097c7c65ee490a758b442acd5c80c6fce4b148c6a391e946b45131365b@54.169.166.226:30303
        # cpp_bootstrap
        #- enode://4a44599974518ea5b0f14c31c4463692ac0329cb84851f3435e6d1b18ee4eae4aa495f846a0fa1219bd58035671881d44423876e57db2abd57254d0197da0ebe@5.1.83.226:30303

p2p:
    num_peers: 10

    listen_host: 0.0.0.0
    listen_port: 30303
node:
    privkey_hex: 65462b0520ef7d3df61b9992ed3bea0c56ead753be7c8b3614e0ce01e4cac41b
    """
    if len(sys.argv) == 1:
        config = yaml.load(io.BytesIO(sample_config))
        pubkey = crypto.privtopub(decode_hex(config['node']['privkey_hex']))
        config['node']['id'] = crypto.sha3(pubkey)
    else:
        fn = sys.argv[1]
        log.info('loading config from', fn=fn)
        config = yaml.load(open(fn))

    # stop on every unhandled exception!
    gevent.get_hub(
    ).SYSTEM_ERROR = BaseException  # (KeyboardInterrupt, SystemExit, SystemError)

    print(config)
    # create app
    app = BaseApp(config)

    # register services
    NodeDiscovery.register_with_app(app)
    PeerManager.register_with_app(app)

    # start app
    app.start()

    # wait for interupt
    evt = gevent.event.Event()
    # gevent.signal(signal.SIGQUIT, gevent.kill) ## killall pattern
    gevent.signal(signal.SIGQUIT, evt.set)
    gevent.signal(signal.SIGTERM, evt.set)
    gevent.signal(signal.SIGINT, evt.set)
    evt.wait()

    # finally stop
    app.stop()
from devp2p.protocol import BaseProtocol
from devp2p.p2p_protocol import P2PProtocol
from devp2p import kademlia
from devp2p.peer import Peer
from devp2p import crypto
from devp2p import utils
from devp2p import discovery
from devp2p import slogging
import sys

#log = slogging.get_logger('p2p.peermgr')
slogging.configure(':DEBUG')
root_logger = slogging.getLogger()
if root_logger.handlers == []:
    root_logger.addHandler(slogging.logging.StreamHandler(sys.stderr))
log = slogging.get_logger('p2p')

from collections import defaultdict


class ETHProtocol(BaseProtocol):
    """
    DEV Ethereum Wire Protocol
    https://github.com/ethereum/wiki/wiki/Ethereum-Wire-Protocol
    https://github.com/ethereum/go-ethereum/blob/develop/eth/protocol.go#L15
    """
    protocol_id = 1
    network_id = 0
    max_cmd_id = 15  # FIXME
    name = 'eth'
    version = 63