Exemplo n.º 1
0
def main():
    parser = argparse.ArgumentParser(description='Interact with an electrum server')
    parser.add_argument('method', default=None,
                        help='"blockchain.numblocks.subscribe" or similar')
    parser.add_argument('args', nargs="*", default=[],
                        help='Arguments for method')
    parser.add_argument('--server', default='cluelessperson.com',
                        help='Hostname of Electrum server to use')
    parser.add_argument('--protocol', default='s',
                        help='Protocol code: t=TCP Cleartext, s=SSL, etc')
    parser.add_argument('--port', default=None,
                        help='Port number to override default for protocol')
    parser.add_argument('--tor', default=False, action="store_true",
                        help='Use local Tor proxy to connect')

    args = parser.parse_args()

    import logging
    logging.getLogger('connectrum').setLevel(logging.DEBUG)

    # convert to our datastruct about servers.
    svr = ServerInfo(args.server, args.server,
                    ports=((args.protocol+str(args.port)) if args.port else args.protocol))

    loop = asyncio.get_event_loop()  

    conn = StratumClient()
    connector = conn.connect(svr, args.protocol, use_tor=svr.is_onion, disable_cert_verify=True)

    loop.run_until_complete(interact(conn, svr, connector, args.method, args.args))

    loop.close()
Exemplo n.º 2
0
async def setup_client() -> StratumClient:
    try:
        return CLIENT
    except NameError:
        pass

    server = ServerInfo({
        "nickname": None,
        "hostname": "fortress.qtornado.com",
        "ip_addr": None,
        "ports": ["s50002", "t50001"],
        "version": "1.4",
        "pruning_limit": 0,
        "seen_at": 1533670768.8676858
    })

    client = StratumClient()

    await asyncio.wait_for(client.connect(server_info=server,
                                          proto_code='s',
                                          use_tor=False,
                                          disable_cert_verify=True),
                           timeout=5)
    #
    # await asyncio.wait_for(
    #     client.RPC(
    #         'server.version',
    #         'bitcoin-spv-merkle',
    #         '1.2'),
    #     timeout=5)

    return client
Exemplo n.º 3
0
async def probe(svr, proto_code, use_tor):
    conn = StratumClient()

    try:
        await conn.connect(svr, proto_code, use_tor=(svr.is_onion or use_tor), short_term=True)
    except:
        failed.add(str(svr))
        return None

    peers, _ = conn.subscribe('server.peers.subscribe')

    peers = await peers
    print("%s gave %d peers" % (svr, len(peers)))

    connected.add(str(svr))

    # track them all.
    more = ks.add_peer_response(peers)

    if more:
        print("found %d more servers from %s: %s" % (len(more), svr, ', '.join(more)))
    

    conn.close()

    return str(svr)
Exemplo n.º 4
0
async def setup_client():
    if CLIENT is not None:
        return CLIENT
    server = ServerInfo({
        "nickname": None,
        "hostname": "bitcoin.cluelessperson.com",
        "ip_addr": "172.92.140.254",
        "ports": ["s50002", "t50001"],
        "version": "1.2",
        "pruning_limit": 0,
        "seen_at": 1533670768.588772
    })

    client = StratumClient()

    await asyncio.wait_for(client.connect(server_info=server,
                                          proto_code='s',
                                          use_tor=False,
                                          disable_cert_verify=True),
                           timeout=5)

    await asyncio.wait_for(client.RPC('server.version', 'bitcoin-spv-merkle',
                                      '1.2'),
                           timeout=5)

    return client
Exemplo n.º 5
0
async def probe(svr, proto_code, use_tor):
    conn = StratumClient()

    try:
        await conn.connect(svr,
                           proto_code,
                           use_tor=(svr.is_onion or use_tor),
                           short_term=True)
    except:
        failed.add(str(svr))
        return None

    peers, _ = conn.subscribe('server.peers.subscribe')

    peers = await peers
    print("%s gave %d peers" % (svr, len(peers)))

    connected.add(str(svr))

    # track them all.
    more = ks.add_peer_response(peers)

    if more:
        print("found %d more servers from %s: %s" %
              (len(more), svr, ', '.join(more)))

    conn.close()

    return str(svr)
Exemplo n.º 6
0
    def __init__(self,
                 loop: asyncio.AbstractEventLoop,
                 server: str,
                 port: int,
                 proto: str) -> None:
        """
        Connection object constructor.

        :param loop: an asyncio event loop
        :param server: a string containing a hostname
        :param port: port number that the server listens on
        :returns: A new Connection object
        """
        logging.info("Connecting...")

        self.server_info = ServerInfo(
            server, hostname=server, ports=port)  # type: ServerInfo
        logging.info(str(self.server_info.get_port(proto)))
        self.client = StratumClient(loop)  # type: StratumClient
        self.connection = self.client.connect(
            self.server_info,
            proto_code=proto,
            use_tor=True,
            disable_cert_verify=(proto != "s"))  # type: asyncio.Future

        loop.run_until_complete(self._do_connect())
        self.queue = None  # type: asyncio.Queue
Exemplo n.º 7
0
    async def new_client(self, network: str) -> StratumClient:
        while True:
            server = self._get_server_info(network)
            try:

                client = StratumClient()

                await asyncio.wait_for(
                    client.connect(
                        server_info=server,
                        proto_code='s',
                        use_tor=False,
                        disable_cert_verify=True),
                    timeout=self._timeout_seconds)

                await asyncio.wait_for(
                    client.RPC(
                        'server.version',
                        self.user_agent,
                        self.protocol_version),
                    timeout=self._timeout_seconds)

                asyncio.ensure_future(self._keepalive(client, network))
                self._servers.append(str(server))
                return client

            except Exception as e:
                print('failed:', server)
                print(e, str(e))
                # fall back to top of loop and try a new server
                pass
Exemplo n.º 8
0
def main():
    svr = ServerInfo("test-net", "testnet.hsmiths.com", ports="s50002")
    loop = asyncio.get_event_loop()
    conn = StratumClient()
    connector = conn.connect(svr, disable_cert_verify=True)
    loop.run_until_complete(
        interact(conn, svr, connector, "blockchain.address.get_balance",
                 ["3KF9nXowQ4asSGxRRzeiTpDjMuwM2nypAN"]))
    loop.close()
Exemplo n.º 9
0
 async def connect(self):
     server_info = ServerInfo("", hostname="mdw.ddns.net", ports=50001)
     self.client = StratumClient(self.app.loop)
     self.connection = self.client.connect(
         server_info, proto_code="t")  # type: asyncio.Future
     try:
         await self.connection
         self.connected = True
     except Exception:
         print("Unable to connect to server:", server_info)
Exemplo n.º 10
0
def main():
    parser = argparse.ArgumentParser(description='Subscribe to BTC events')
    parser.add_argument('method',
                        help='"blockchain.numblocks.subscribe" or similar')
    parser.add_argument('args',
                        nargs="*",
                        default=[],
                        help='Arguments for method')
    parser.add_argument('--server',
                        default='cluelessperson.com',
                        help='Hostname of Electrum server to use')
    parser.add_argument('--protocol',
                        default='s',
                        help='Protocol code: t=TCP Cleartext, s=SSL, etc')
    parser.add_argument('--port',
                        default=None,
                        help='Port number to override default for protocol')
    parser.add_argument('--tor',
                        default=False,
                        action="store_true",
                        help='Use local Tor proxy to connect')
    parser.add_argument('--debug',
                        default=False,
                        action="store_true",
                        help='Enable debug output from connectrum library')

    args = parser.parse_args()

    if args.debug:
        import logging
        logging.getLogger('connectrum').setLevel(logging.DEBUG)

    # convert to our datastruct about servers.
    svr = ServerInfo(args.server,
                     args.server,
                     ports=((args.protocol +
                             str(args.port)) if args.port else args.protocol))

    loop = asyncio.get_event_loop()

    conn = StratumClient()
    connector = conn.connect(svr,
                             args.protocol,
                             use_tor=svr.is_onion,
                             disable_cert_verify=True)

    loop.run_until_complete(
        listen(conn, svr, connector, args.method, args.args))

    loop.close()
Exemplo n.º 11
0
async def startup_code(app):
    # pick a random server
    app['conn'] = conn = StratumClient()
    try:
        await conn.connect(el_server,
                           disable_cert_verify=True,
                           use_tor=('localhost',
                                    9150) if el_server.is_onion else False)
    except Exception as exc:
        print("unable to connect: %r" % exc)
        sys.exit()

    print(
        "Connected to electrum server: {hostname}:{port} ssl={ssl} tor={tor} ip_addr={ip_addr}"
        .format(**conn.actual_connection))

    # track top block
    async def track_top_block():
        global top_blk
        fut, Q = conn.subscribe('blockchain.headers.subscribe')
        top_blk = await fut
        while 1:
            top_blk = max(await Q.get())
            print("new top-block: %r" % (top_blk, ))

    app.loop.create_task(track_top_block())
Exemplo n.º 12
0
def call_electrum(conn, method, *args):
    # call a method and format up the response nicely
    print("args")
    print(args)
    svr = ServerInfo("electrumx.tamami-foundation.org",
                     "electrumx.tamami-foundation.org",
                     ports=(("tcp" + str("50001")) if "50001" else "tcp"))
    conn = StratumClient()
    time.sleep(10)
    t = ''
    try:
        resp = conn.RPC(method, *args)
        time.sleep(10)
    except ElectrumErrorResponse as e:
        response, req = e.args
        t += "2-1"
        return t
    print("CALL_ELECTRUM REPONSE")
    print(resp)
    return resp
Exemplo n.º 13
0
async def startup_code(app):
    # pick a random server
    app['conn'] = conn = StratumClient()
    await conn.connect(el_server, disable_cert_verify=True)

    # track top block
    async def track_top_block():
        global top_blk
        fut, Q = conn.subscribe('blockchain.numblocks.subscribe')
        top_blk = await fut
        while 1:
            top_blk = max(await Q.get())
            print("new top-block: %r" % (top_blk, ))

    app.loop.create_task(track_top_block())
Exemplo n.º 14
0
async def find_utxos(server: ServerInfo, master_key: BIP32, max_gap: int,
                     max_account: int, address: Optional[str],
                     fee_rate: Optional[int], should_broadcast: bool):
    """
    Connect to an electrum server and find all the UTXOs spendable by a master key.
    """
    print('⏳  Connecting to electrum server, this might take a while')

    client = StratumClient()
    await client.connect(server, disable_cert_verify=True)

    print('🌍  Connected to electrum server successfully')

    utxos = await scanner.scan_master_key(client, master_key, max_gap,
                                          max_account)

    if len(utxos) == 0:
        print('😔  Didn\'t find any unspent outputs')
        client.close()
        return

    balance = sum([utxo.amount_in_sat for utxo in utxos])
    print(f'💸  Total spendable balance found: {balance} sats')

    if master_key.master_privkey is None:
        print('✍️  Re-run with a private key to create a sweep transaction')
        client.close()
        return

    if address is None:
        print('ℹ️   Re-run with `--address` to create a sweep transaction')
        client.close()
        return

    if fee_rate is None:
        fee_rate_in_btc_per_kb = await client.RPC('blockchain.estimatefee', 1)

        if fee_rate_in_btc_per_kb == -1:
            print(
                '🔁  Couldn\'t fetch fee rates, try again with manual fee rates using `--fee-rate`'
            )
            client.close()
            return

        fee_rate = int(fee_rate_in_btc_per_kb * 10**8 / 1024)

        print(f'🚌  Fetched next-block fee rate of {fee_rate} sat/vbyte')

    tx_without_fee = transactions.Transaction(master_key, utxos, address,
                                              balance)
    fee = tx_without_fee.virtual_size() * fee_rate
    tx = transactions.Transaction(master_key, utxos, address, balance - fee)
    bin_tx = tx.to_bytes()

    print('👇  This transaction sweeps all funds to the address provided')
    print()
    print(bin_tx.hex())
    print()

    if not should_broadcast:
        print(
            '📋  Copy this transaction and broadcast it manually to the network, or re-run with `--broadcast`'
        )
        client.close()
        return

    try:
        print('📣  Broadcasting transaction to the network')
        txid = await client.RPC('blockchain.transaction.broadcast',
                                bin_tx.hex())
        print(f'✅  Transaction {txid} successfully broadcasted')
    except connectrum.exc.ElectrumErrorResponse as err:
        print(f'⛔️  Transaction broadcasting failed: {err}')

    client.close()
Exemplo n.º 15
0
class Connection:
    """ Connection object. Connects to an Electrum server, and handles all
    Stratum protocol messages.
    """

    #  pylint: disable=E1111
    def __init__(self, loop: asyncio.AbstractEventLoop, server: str, port: int,
                 proto: str) -> None:
        """ Connection object constructor.

        :param loop: an asyncio event loop
        :param server: a string containing a hostname
        :param port: port number that the server listens on
        :returns: A new Connection object
        """
        logging.info("Connecting...")

        self.server_info = ServerInfo(server, hostname=server,
                                      ports=port)  # type: ServerInfo

        logging.info(str(self.server_info.get_port(proto)))

        self.client = StratumClient(loop)  # type: StratumClient
        self.connection = self.client.connect(
            self.server_info,
            proto_code=proto,
            use_tor=True,
            disable_cert_verify=(proto != "s"))  # type: asyncio.Future

        self.queue = None  # type: asyncio.Queue

    async def do_connect(self) -> None:
        """ Coroutine. Establishes a persistent connection to an Electrum server.
        Awaits the connection because AFAIK an init method can't be async.
        """
        await self.connection
        logging.info("Connected to server")

    async def listen_rpc(self, method: str, args: List) -> Any:
        """ Coroutine. Sends a normal RPC message to the server and awaits response.

        :param method: The Electrum API method to use
        :param args: Params associated with current method
        :returns: Future. Response from server for this method(args)
        """
        return await self.client.RPC(method, *args)

    def listen_subscribe(self, method: str, args: List) -> None:
        """ Sends a "subscribe" message to the server and adds to the queue.
        Throws away the immediate future containing the "history" hash.

        :param method: The Electrum API method to use
        :param args: Params associated with current method
        """
        t = self.client.subscribe(
            method, *args)  # type: Tuple[asyncio.Future, asyncio.Queue]
        future, queue = t

        self.queue = queue
        return future

    async def consume_queue(
            self, queue_func: Callable[[List[str]], Awaitable[None]]) -> None:
        """ Coroutine. Infinite loop that consumes the current subscription queue.
        :param queue_func: A function to call when new responses arrive
        """
        while True:
            result = await self.queue.get()  # type: List[str]
            await queue_func(result)
Exemplo n.º 16
0
class Server:
    def __init__(self, _chain):
        self.chain = _chain
        self.client = None
        self.connection = None
        self.server_list = None
        self.app = web.Application()
        self.app.router.add_get('/servers', self.handle)
        self.app.on_startup.append(self.start_background_tasks)
        self.app.on_cleanup.append(self.cleanup_background_tasks)
        self.load_server_list()
        self.connected = False

    def load_server_list(self):
        try:
            with open("servers.json", "r") as infile:
                self.server_list = json.load(infile)
        except Exception:
            self.server_list = list()

    async def connect(self):
        server_info = ServerInfo("", hostname="mdw.ddns.net", ports=50001)
        self.client = StratumClient(self.app.loop)
        self.connection = self.client.connect(
            server_info, proto_code="t")  # type: asyncio.Future
        try:
            await self.connection
            self.connected = True
        except Exception:
            print("Unable to connect to server:", server_info)

    async def get_peers(self):
        server_list = list()
        peers = await self.client.RPC("server.peers.subscribe")
        for peer in peers:
            host, info = peer[1:]
            if info[0] not in ("v1.1", "v1.2"):
                continue
            proto_port = info[1]
            proto, port = proto_port[0], int(proto_port[1:])
            server = [host, port, proto]
            server_list.append(server)
        return server_list

    async def update_server_list(self):
        if self.connected:
            self.server_list = await self.get_peers()
        else:
            self.server_list = await scrape_electrum_servers(
                self.chain, loop=self.app.loop)
        with open("servers.json", "w") as outfile:
            json.dump(self.server_list, outfile)

    async def update_loop(self):
        while True:
            if self.connected and not self.client.protocol:
                self.connected = False
            if not self.connected:
                pass  # await self.connect()
            await self.update_server_list()
            await asyncio.sleep(600)

    async def start_background_tasks(self, app):
        app['dispatch'] = asyncio.ensure_future(self.update_loop())

    async def cleanup_background_tasks(self, app):
        app['dispatch'].cancel()
        await app['dispatch']

    async def handle(self, request):
        response_obj = {'servers': self.server_list}
        return web.json_response(response_obj)