From 4fedaee51cae3b261937882f4e40e3afa02aee87 Mon Sep 17 00:00:00 2001 From: Jacob Plaster Date: Mon, 3 Dec 2018 14:41:30 +0000 Subject: [PATCH] Add authentication for rest client --- bfxapi/rest/BfxRest.py | 33 +++++++++++++++++++++++--- bfxapi/utils/auth.py | 39 +++++++++++++++++++++++++++++++ bfxapi/websockets/BfxWebsocket.py | 16 ++----------- 3 files changed, 71 insertions(+), 17 deletions(-) create mode 100644 bfxapi/utils/auth.py diff --git a/bfxapi/rest/BfxRest.py b/bfxapi/rest/BfxRest.py index aad0f97..47bd8f9 100644 --- a/bfxapi/rest/BfxRest.py +++ b/bfxapi/rest/BfxRest.py @@ -4,12 +4,16 @@ import time import json from ..utils.CustomLogger import CustomLogger +from ..utils.auth import generate_auth_headers +from ..models import Wallet class BfxRest: def __init__(self, API_KEY, API_SECRET, host='https://api.bitfinex.com/v2', loop=None, logLevel='INFO', *args, **kwargs): self.loop = loop or asyncio.get_event_loop() + self.API_KEY = API_KEY + self.API_SECRET = API_SECRET self.host = host self.logger = CustomLogger('BfxRest', logLevel=logLevel) @@ -19,9 +23,23 @@ class BfxRest: async with session.get(url) as resp: text = await resp.text() if resp.status is not 200: - raise Exception('Unable to seed trades. Received status {} - {}' - .format(resp.status, text)) - return json.loads(text) + raise Exception('GET {} failed with status {} - {}' + .format(url, resp.status, text)) + return await resp.json(text) + + async def post(self, endpoint, data={}): + url = '{}/{}'.format(self.host, endpoint) + sData = json.dumps(data) + headers = generate_auth_headers( + self.API_KEY, self.API_SECRET, endpoint, sData) + headers["content-type"] = "application/json" + async with aiohttp.ClientSession() as session: + async with session.post(url, headers=headers, data=sData) as resp: + text = await resp.text() + if resp.status is not 200: + raise Exception('POST {} failed with status {} - {}' + .format(url, resp.status, text)) + return await resp.json() async def get_seed_candles(self, symbol): endpoint = 'candles/trade:1m:{}/hist?limit=5000&_bfx=1'.format(symbol) @@ -43,3 +61,12 @@ class BfxRest: candles.sort(key=lambda x: x[0], reverse=True) self.logger.info("Downloaded {} candles.".format(len(candles))) return candles + + ################################################## + # Wallets # + ################################################## + + async def get_wallets(self): + endpoint = "auth/r/wallets" + raw_wallets = await self.post(endpoint) + return [ Wallet(rw[0], rw[1], rw[2], rw[3]) for rw in raw_wallets ] diff --git a/bfxapi/utils/auth.py b/bfxapi/utils/auth.py new file mode 100644 index 0000000..358c953 --- /dev/null +++ b/bfxapi/utils/auth.py @@ -0,0 +1,39 @@ +import hashlib +import hmac +import time + +def generate_auth_payload(API_KEY, API_SECRET): + nonce = _gen_nonce() + authMsg, sig = _gen_signature(API_KEY, API_SECRET, nonce) + + return { + 'apiKey': API_KEY, + 'authSig': sig, + 'authNonce': nonce, + 'authPayload': authMsg, + 'event': 'auth' + } + +def generate_auth_headers(API_KEY, API_SECRET, path, body): + nonce = str(_gen_nonce()) + signature = "/api/v2/{}{}{}".format(path, nonce, body) + print (API_KEY) + print (API_SECRET) + h = hmac.new(API_SECRET.encode('utf8'), signature.encode('utf8'), hashlib.sha384) + signature = h.hexdigest() + + return { + "bfx-nonce": nonce, + "bfx-apikey": API_KEY, + "bfx-signature": signature + } + +def _gen_signature(API_KEY, API_SECRET, nonce): + authMsg = 'AUTH{}'.format(nonce) + secret = API_SECRET.encode('utf8') + sig = hmac.new(secret, authMsg.encode('utf8'), hashlib.sha384).hexdigest() + + return authMsg, sig + +def _gen_nonce(): + return int(round(time.time() * 1000000)) diff --git a/bfxapi/websockets/BfxWebsocket.py b/bfxapi/websockets/BfxWebsocket.py index e1081bd..cb04890 100644 --- a/bfxapi/websockets/BfxWebsocket.py +++ b/bfxapi/websockets/BfxWebsocket.py @@ -1,14 +1,13 @@ import asyncio import json import time -import hashlib -import hmac import random from .GenericWebsocket import GenericWebsocket, AuthError from .SubscriptionManager import SubscriptionManager from .WalletManager import WalletManager from .OrderManager import OrderManager +from ..utils.auth import generate_auth_payload from ..models import Order, Trade, OrderBook class Flags: @@ -392,18 +391,7 @@ class BfxWebsocket(GenericWebsocket): self.logger.warn('Unknown websocket response: {}'.format(msg)) async def _ws_authenticate_socket(self): - nonce = int(round(time.time() * 1000000)) - authMsg = 'AUTH{}'.format(nonce) - secret = self.API_SECRET.encode() - sig = hmac.new(secret, authMsg.encode(), hashlib.sha384).hexdigest() - hmac.new(secret, self.API_SECRET.encode('utf'), hashlib.sha384).hexdigest() - jdata = { - 'apiKey': self.API_KEY, - 'authSig': sig, - 'authNonce': nonce, - 'authPayload': authMsg, - 'event': 'auth' - } + jdata = generate_auth_payload(self.API_KEY, self.API_SECRET) await self.ws.send(json.dumps(jdata)) async def on_open(self):