diff --git a/pisa-btc/pisa/api.py b/pisa-btc/pisa/api.py index 9a9d3d2..9cb0efb 100644 --- a/pisa-btc/pisa/api.py +++ b/pisa-btc/pisa/api.py @@ -2,7 +2,7 @@ from pisa import * from pisa.watcher import Watcher from pisa.inspector import Inspector from pisa.appointment import Appointment -from flask import Flask, request, Response +from flask import Flask, request, Response, abort import json app = Flask(__name__) @@ -49,6 +49,63 @@ def add_appointment(): return Response(response, status=rcode, mimetype='text/plain') +# FIXME: THE NEXT TWO API ENDPOINTS ARE FOR TESTING AND SHOULD BE REMOVED / PROPERLY MANAGED BEFORE PRODUCTION! +@app.route('/get_appointment', methods=['GET']) +def get_appointment(): + locator = request.args.get('locator') + response = [] + + job_in_watcher = watcher.appointments.get(locator) + + if job_in_watcher: + for job in job_in_watcher: + job_data = job.to_json() + job_data['status'] = "being watched" + response.append(job_data) + + if watcher.responder: + responder_jobs = watcher.responder.jobs + + for job_id, job in responder_jobs.items(): + if job.locator == locator: + job_data = job.to_json() + job_data['status'] = "dispute responded" + job_data['confirmations'] = watcher.responder.confirmation_counter.get(job_id) + response.append(job_data) + + if not response: + response.append({"locator": locator, "status": "not found"}) + + response = json.dumps(response) + + return response + + +@app.route('/get_all_appointments', methods=['GET']) +def get_all_appointments(): + watcher_appointments = [] + responder_jobs = [] + + if request.remote_addr in ['localhost', '127.0.0.1']: + for app_id, appointment in watcher.appointments.items(): + jobs_data = [job.to_json() for job in appointment] + + watcher_appointments.append({app_id: jobs_data}) + + if watcher.responder: + for job_id, job in watcher.responder.jobs.items(): + job_data = job.to_json() + job_data['confirmations'] = watcher.responder.confirmation_counter.get(job_id) + responder_jobs.append({job_id: job_data}) + + response = json.dumps({"watcher_appointments": watcher_appointments, "responder_jobs": responder_jobs}) + + else: + abort(404) + + return response + + def start_api(d, l): # FIXME: Pretty ugly but I haven't found a proper way to pass it to add_appointment global debug, logging, watcher, inspector diff --git a/pisa-btc/pisa/appointment.py b/pisa-btc/pisa/appointment.py index 69c4558..bccee5f 100644 --- a/pisa-btc/pisa/appointment.py +++ b/pisa-btc/pisa/appointment.py @@ -12,6 +12,13 @@ class Appointment: self.cipher = cipher self.hash_function = hash_function + def to_json(self): + appointment = {"locator": self.locator, "start_time": self.start_time, "end_time": self.end_time, + "dispute_delta": self.dispute_delta, "encrypted_blob": self.encrypted_blob.data, + "cipher": self.cipher, "hash_function": self.hash_function} + + return appointment + # ToDO: We may want to add some additional things to the appointment, like # minimum fee # refund to be payed to the user in case of failing diff --git a/pisa-btc/pisa/responder.py b/pisa-btc/pisa/responder.py index ca34fd4..e607eee 100644 --- a/pisa-btc/pisa/responder.py +++ b/pisa-btc/pisa/responder.py @@ -1,5 +1,7 @@ from queue import Queue from threading import Thread +from hashlib import sha256 +from binascii import unhexlify from pisa.zmq_subscriber import ZMQHandler from pisa.rpc_errors import * from pisa.tools import check_tx_in_chain @@ -13,11 +15,20 @@ MIN_CONFIRMATIONS = 6 class Job: def __init__(self, dispute_txid, justice_rawtx, appointment_end, retry_counter=0): self.dispute_txid = dispute_txid + # FIXME: locator is here so we can give info about jobs for now. It can be either passed from watcher or info + # can be directly got from DB + self.locator = sha256(unhexlify(dispute_txid)).hexdigest() self.justice_rawtx = justice_rawtx self.appointment_end = appointment_end self.missed_confirmations = 0 self.retry_counter = retry_counter + def to_json(self): + job = {"locator": self.dispute_txid, "justice_rawtx": self.justice_rawtx, + "appointment_end": self.appointment_end} + + return job + class Responder: def __init__(self): @@ -215,4 +226,4 @@ class Responder: # reorg manager logging.warning("[Responder] dispute and justice transaction missing. Calling the reorg manager") logging.error("[Responder] reorg manager not yet implemented") - pass \ No newline at end of file + pass diff --git a/pisa-btc/pisa/watcher.py b/pisa-btc/pisa/watcher.py index ed78f23..04c7b56 100644 --- a/pisa-btc/pisa/watcher.py +++ b/pisa-btc/pisa/watcher.py @@ -16,6 +16,7 @@ class Watcher: self.asleep = True self.max_appointments = max_appointments self.zmq_subscriber = None + self.responder = Responder() def add_appointment(self, appointment, debug, logging): # DISCUSS: about validation of input data @@ -40,8 +41,7 @@ class Watcher: self.asleep = False self.block_queue = Queue() zmq_thread = Thread(target=self.do_subscribe, args=[self.block_queue, debug, logging]) - responder = Responder() - watcher = Thread(target=self.do_watch, args=[responder, debug, logging]) + watcher = Thread(target=self.do_watch, args=[debug, logging]) zmq_thread.start() watcher.start() @@ -66,7 +66,7 @@ class Watcher: self.zmq_subscriber = ZMQHandler(parent='Watcher') self.zmq_subscriber.handle(block_queue, debug, logging) - def do_watch(self, responder, debug, logging): + def do_watch(self, debug, logging): bitcoin_cli = AuthServiceProxy("http://%s:%s@%s:%d" % (BTC_RPC_USER, BTC_RPC_PASSWD, BTC_RPC_HOST, BTC_RPC_PORT)) @@ -131,8 +131,8 @@ class Watcher: logging.info("[Watcher] notifying responder about {} and deleting appointment {}:{}".format( justice_txid, locator, appointment_pos)) - responder.add_response(dispute_txid, justice_txid, justice_rawtx, - self.appointments[locator][appointment_pos].end_time, debug, logging) + self.responder.add_response(dispute_txid, justice_txid, justice_rawtx, + self.appointments[locator][appointment_pos].end_time, debug, logging) # If there was only one appointment that matches the locator we can delete the whole list # DISCUSS: We may want to use locks before adding / removing appointment