You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
bitcoind-ncurses2/rpc.py

114 lines
3.4 KiB
Python

# Copyright (c) 2014-2017 esotericnonsense (Daniel Edgecumbe)
# Distributed under the MIT software license, see the accompanying
# file COPYING or https://opensource.org/licenses/mit-license.php
import aiohttp
import async_timeout
import base64
import os
import ujson as json
import config
def craft_url(proto, ip, port):
return "{}://{}:{}".format(proto, ip, port)
def get_url_from_datadir(datadir):
configfile = os.path.join(datadir, "bitcoin.conf")
try:
cfg = config.parse_file(configfile)
except IOError:
return craft_url("http", "localhost", 8332)
proto = cfg["protocol"] if "protocol" in cfg else "http"
ip = cfg["rpcip"] if "rpcip" in cfg else "localhost"
try:
port = cfg["rpcport"]
except KeyError:
# If both regtest and testnet are set, bitcoind will not run.
if "regtest" in cfg and cfg["regtest"] == "1":
port = 18332
elif "testnet" in cfg and cfg["testnet"] == "1":
port = 18332
else:
port = 8332
return craft_url(proto, ip, port)
def get_auth_from_datadir(datadir):
def craft_auth_from_credentials(user, password):
details = ":".join([user, password])
return base64.b64encode(bytes(details, "utf-8")).decode("utf-8")
def get_auth_from_cookiefile(cookiefile):
# Raises IOError if file does not exist
with open(cookiefile, "r") as f:
return base64.b64encode(bytes(f.readline(), "utf-8")).decode("utf-8")
cookiefile = os.path.join(datadir, ".cookie")
try:
return get_auth_from_cookiefile(cookiefile)
except FileNotFoundError:
print("cookiefile not found, falling back to password authentication")
# Fall back to credential-based authentication
configfile = os.path.join(datadir, "bitcoin.conf")
try:
cfg = config.parse_file(configfile)
except IOError:
print("configuration file not found; aborting.")
raise
try:
rpcuser = cfg["rpcuser"]
rpcpassword = cfg["rpcpassword"]
except KeyError:
if not ("rpcuser" in cfg):
print("rpcuser not in configuration file.")
if not ("rpcpassword" in cfg):
print("rpcpassword not in configuration file.")
raise
return craft_auth_from_credentials(rpcuser, rpcpassword)
class BitcoinRPCClient(object):
def __init__(self, url, auth):
self._url = url
self._headers = {
"Authorization": "Basic {}".format(auth),
"Content-Type": "text/plain",
}
@staticmethod
async def _craft_request(req, params, ident):
d = {
# "jsonrpc": "2.0", # Currently ignored by Bitcoin Core.
"method": req,
}
if params is not None:
d["params"] = params
if ident is not None:
d["id"] = ident
return json.dumps(d)
async def _fetch(self, session, req):
with async_timeout.timeout(5):
async with session.post(self._url, headers=self._headers, data=req) as response:
return await response.text()
async def request(self, method, params=None, ident=None, callback=None):
async with aiohttp.ClientSession() as session:
req = await self._craft_request(method, params, ident)
html = await self._fetch(session, req)
return json.loads(html)