mirror of
https://github.com/aljazceru/python-teos.git
synced 2025-12-18 14:44:21 +01:00
Merge pull request #119 from orbitalturtle/cli_get_all_appointments
Add get_all_appointments cli command
This commit is contained in:
@@ -104,7 +104,17 @@ if `-f, --file` **is** specified, then the command expects a path to a json file
|
|||||||
|
|
||||||
python teos_cli.py get_appointment <appointment_locator>
|
python teos_cli.py get_appointment <appointment_locator>
|
||||||
|
|
||||||
|
### get_all_appointments
|
||||||
|
|
||||||
|
This command is used to get information about all the appointments stored in a Eye of Satoshi tower.
|
||||||
|
|
||||||
|
**Responses**
|
||||||
|
|
||||||
|
This command returns all appointments stored in the watchtower. More precisely, it returns all the "response_trackers" and "watchtower_appointments" in a dictionary.
|
||||||
|
|
||||||
|
#### Usage
|
||||||
|
|
||||||
|
python teos_cli.py get_all_appointments
|
||||||
|
|
||||||
### help
|
### help
|
||||||
|
|
||||||
|
|||||||
12
cli/help.py
12
cli/help.py
@@ -6,6 +6,7 @@ def show_usage():
|
|||||||
"\n\tregister \tRegisters your user public key with the tower."
|
"\n\tregister \tRegisters your user public key with the tower."
|
||||||
"\n\tadd_appointment \tRegisters a json formatted appointment with the tower."
|
"\n\tadd_appointment \tRegisters a json formatted appointment with the tower."
|
||||||
"\n\tget_appointment \tGets json formatted data about an appointment from the tower."
|
"\n\tget_appointment \tGets json formatted data about an appointment from the tower."
|
||||||
|
"\n\tget_all_appointments \tGets information about all appointments stored in the tower."
|
||||||
"\n\thelp \t\t\tShows a list of commands or help for a specific command."
|
"\n\thelp \t\t\tShows a list of commands or help for a specific command."
|
||||||
"\n\nGLOBAL OPTIONS:"
|
"\n\nGLOBAL OPTIONS:"
|
||||||
"\n\t-s, --server \tAPI server where to send the requests. Defaults to 'localhost' (modifiable in conf file)."
|
"\n\t-s, --server \tAPI server where to send the requests. Defaults to 'localhost' (modifiable in conf file)."
|
||||||
@@ -51,3 +52,14 @@ def help_get_appointment():
|
|||||||
"\n\nDESCRIPTION:"
|
"\n\nDESCRIPTION:"
|
||||||
"\n\n\tGets json formatted data about an appointment from the tower.\n"
|
"\n\n\tGets json formatted data about an appointment from the tower.\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def help_get_all_appointments():
|
||||||
|
return (
|
||||||
|
"NAME:"
|
||||||
|
"\tpython teos_cli get_all_appointments - Gets information about all appointments stored in the tower."
|
||||||
|
"\n\nUSAGE:"
|
||||||
|
"\tpython teos_cli.py get_all_appointments"
|
||||||
|
"\n\nDESCRIPTION:"
|
||||||
|
"\n\n\tGets information about all appointments stored in the tower.\n"
|
||||||
|
)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from getopt import getopt, GetoptError
|
|||||||
from requests import ConnectTimeout, ConnectionError
|
from requests import ConnectTimeout, ConnectionError
|
||||||
from requests.exceptions import MissingSchema, InvalidSchema, InvalidURL
|
from requests.exceptions import MissingSchema, InvalidSchema, InvalidURL
|
||||||
|
|
||||||
from cli.help import show_usage, help_add_appointment, help_get_appointment, help_register
|
from cli.help import show_usage, help_add_appointment, help_get_appointment, help_register, help_get_all_appointments
|
||||||
from cli import DEFAULT_CONF, DATA_DIR, CONF_FILE_NAME, LOG_PREFIX
|
from cli import DEFAULT_CONF, DATA_DIR, CONF_FILE_NAME, LOG_PREFIX
|
||||||
|
|
||||||
import common.cryptographer
|
import common.cryptographer
|
||||||
@@ -175,6 +175,39 @@ def get_appointment(locator, cli_sk, teos_pk, teos_url):
|
|||||||
return response_json
|
return response_json
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_appointments(teos_url):
|
||||||
|
"""
|
||||||
|
Gets information about all appointments stored in the tower, if the user requesting the data is an administrator.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
teos_url (:obj:`str`): the teos base url.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
:obj:`dict` a dictionary containing all the appointments stored by the Responder and Watcher if the tower
|
||||||
|
responds.
|
||||||
|
"""
|
||||||
|
|
||||||
|
get_all_appointments_endpoint = "{}/get_all_appointments".format(teos_url)
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(url=get_all_appointments_endpoint, timeout=5)
|
||||||
|
|
||||||
|
if response.status_code != constants.HTTP_OK:
|
||||||
|
logger.error("The server returned an error", status_code=response.status_code, reason=response.reason)
|
||||||
|
return None
|
||||||
|
|
||||||
|
response_json = json.dumps(response.json(), indent=4, sort_keys=True)
|
||||||
|
return response_json
|
||||||
|
|
||||||
|
except ConnectionError:
|
||||||
|
logger.error("Can't connect to the Eye of Satoshi's API. Server cannot be reached")
|
||||||
|
return None
|
||||||
|
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
logger.error("The request timed out")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def load_keys(teos_pk_path, cli_sk_path, cli_pk_path):
|
def load_keys(teos_pk_path, cli_sk_path, cli_pk_path):
|
||||||
"""
|
"""
|
||||||
Loads all the keys required so sign, send, and verify the appointment.
|
Loads all the keys required so sign, send, and verify the appointment.
|
||||||
@@ -426,6 +459,11 @@ def main(args, command_line_conf):
|
|||||||
if appointment_data:
|
if appointment_data:
|
||||||
print(appointment_data)
|
print(appointment_data)
|
||||||
|
|
||||||
|
elif command == "get_all_appointments":
|
||||||
|
appointment_data = get_all_appointments(teos_url)
|
||||||
|
if appointment_data:
|
||||||
|
print(appointment_data)
|
||||||
|
|
||||||
elif command == "help":
|
elif command == "help":
|
||||||
if args:
|
if args:
|
||||||
command = args.pop(0)
|
command = args.pop(0)
|
||||||
@@ -442,6 +480,9 @@ def main(args, command_line_conf):
|
|||||||
else:
|
else:
|
||||||
logger.error("Unknown command. Use help to check the list of available commands")
|
logger.error("Unknown command. Use help to check the list of available commands")
|
||||||
|
|
||||||
|
elif command == "get_all_appointments":
|
||||||
|
sys.exit(help_get_all_appointments())
|
||||||
|
|
||||||
else:
|
else:
|
||||||
sys.exit(show_usage())
|
sys.exit(show_usage())
|
||||||
|
|
||||||
@@ -457,7 +498,7 @@ def main(args, command_line_conf):
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
command_line_conf = {}
|
command_line_conf = {}
|
||||||
commands = ["register", "add_appointment", "get_appointment", "help"]
|
commands = ["register", "add_appointment", "get_appointment", "get_all_appointments", "help"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
opts, args = getopt(argv[1:], "s:p:h", ["server", "port", "help"])
|
opts, args = getopt(argv[1:], "s:p:h", ["server", "port", "help"])
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import shutil
|
|||||||
import responses
|
import responses
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
from coincurve import PrivateKey
|
from coincurve import PrivateKey
|
||||||
from requests.exceptions import ConnectionError
|
from requests.exceptions import ConnectionError, Timeout
|
||||||
|
|
||||||
import common.cryptographer
|
import common.cryptographer
|
||||||
from common.logger import Logger
|
from common.logger import Logger
|
||||||
@@ -31,6 +31,7 @@ teos_url = "http://{}:{}".format(config.get("TEOS_SERVER"), config.get("TEOS_POR
|
|||||||
add_appointment_endpoint = "{}/add_appointment".format(teos_url)
|
add_appointment_endpoint = "{}/add_appointment".format(teos_url)
|
||||||
register_endpoint = "{}/register".format(teos_url)
|
register_endpoint = "{}/register".format(teos_url)
|
||||||
get_appointment_endpoint = "{}/get_appointment".format(teos_url)
|
get_appointment_endpoint = "{}/get_appointment".format(teos_url)
|
||||||
|
get_all_appointments_endpoint = "{}/get_all_appointments".format(teos_url)
|
||||||
|
|
||||||
dummy_appointment_data = {
|
dummy_appointment_data = {
|
||||||
"tx": get_random_value_hex(192),
|
"tx": get_random_value_hex(192),
|
||||||
@@ -194,7 +195,7 @@ def test_post_request():
|
|||||||
|
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_process_post_response():
|
def test_process_post_response():
|
||||||
# Let's first crete a response
|
# Let's first create a response
|
||||||
response = {
|
response = {
|
||||||
"locator": dummy_appointment.to_dict()["locator"],
|
"locator": dummy_appointment.to_dict()["locator"],
|
||||||
"signature": get_signature(dummy_appointment.serialize(), dummy_teos_sk),
|
"signature": get_signature(dummy_appointment.serialize(), dummy_teos_sk),
|
||||||
@@ -257,3 +258,37 @@ def test_save_appointment_receipt(monkeypatch):
|
|||||||
assert any([dummy_appointment.locator in f for f in files])
|
assert any([dummy_appointment.locator in f for f in files])
|
||||||
|
|
||||||
shutil.rmtree(appointments_folder)
|
shutil.rmtree(appointments_folder)
|
||||||
|
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_all_appointments():
|
||||||
|
# Response of get_all_appointments endpoint is all appointments from watcher and responder.
|
||||||
|
dummy_appointment_dict["status"] = "being_watched"
|
||||||
|
response = {"watcher_appointments": dummy_appointment_dict, "responder_trackers": {}}
|
||||||
|
|
||||||
|
request_url = get_all_appointments_endpoint
|
||||||
|
responses.add(responses.GET, request_url, json=response, status=200)
|
||||||
|
result = teos_cli.get_all_appointments(teos_url)
|
||||||
|
|
||||||
|
assert len(responses.calls) == 1
|
||||||
|
assert responses.calls[0].request.url == request_url
|
||||||
|
assert json.loads(result).get("locator") == response.get("locator")
|
||||||
|
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_all_appointments_err():
|
||||||
|
# Test that get_all_appointments handles a connection error appropriately.
|
||||||
|
request_url = get_all_appointments_endpoint
|
||||||
|
responses.add(responses.GET, request_url, body=ConnectionError())
|
||||||
|
|
||||||
|
assert not teos_cli.get_all_appointments(teos_url)
|
||||||
|
|
||||||
|
# Test that get_all_appointments handles a timeout error appropriately.
|
||||||
|
responses.replace(responses.GET, request_url, body=Timeout())
|
||||||
|
|
||||||
|
assert not teos_cli.get_all_appointments(teos_url)
|
||||||
|
|
||||||
|
# Test that get_all_appointments handles a 404 error appropriately.
|
||||||
|
responses.replace(responses.GET, request_url, status=404)
|
||||||
|
|
||||||
|
assert teos_cli.get_all_appointments(teos_url) is None
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ def prng_seed():
|
|||||||
def setup_node(bitcoin_cli):
|
def setup_node(bitcoin_cli):
|
||||||
# This method will create a new address a mine bitcoin so the node can be used for testing
|
# This method will create a new address a mine bitcoin so the node can be used for testing
|
||||||
new_addr = bitcoin_cli.getnewaddress()
|
new_addr = bitcoin_cli.getnewaddress()
|
||||||
bitcoin_cli.generatetoaddress(101, new_addr)
|
bitcoin_cli.generatetoaddress(106, new_addr)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
@@ -60,6 +60,31 @@ def create_txs(bitcoin_cli):
|
|||||||
return signed_commitment_tx, signed_penalty_tx
|
return signed_commitment_tx, signed_penalty_tx
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def create_five_txs(bitcoin_cli):
|
||||||
|
utxos = bitcoin_cli.listunspent()
|
||||||
|
|
||||||
|
signed_commitment_txs = []
|
||||||
|
signed_penalty_txs = []
|
||||||
|
|
||||||
|
for i in range(5):
|
||||||
|
if len(utxos) == 0:
|
||||||
|
raise ValueError("There're no UTXOs.")
|
||||||
|
|
||||||
|
utxo = utxos.pop(0)
|
||||||
|
while utxo.get("amount") < Decimal(2 / pow(10, 5)):
|
||||||
|
utxo = utxos.pop(0)
|
||||||
|
|
||||||
|
signed_commitment_tx = create_commitment_tx(bitcoin_cli, utxo)
|
||||||
|
|
||||||
|
signed_commitment_txs.append(signed_commitment_tx)
|
||||||
|
decoded_commitment_tx = bitcoin_cli.decoderawtransaction(signed_commitment_tx)
|
||||||
|
|
||||||
|
signed_penalty_txs.append(create_penalty_tx(bitcoin_cli, decoded_commitment_tx))
|
||||||
|
|
||||||
|
return signed_commitment_txs, signed_penalty_txs
|
||||||
|
|
||||||
|
|
||||||
def run_teosd():
|
def run_teosd():
|
||||||
teosd_process = Process(target=main, kwargs={"command_line_conf": {}}, daemon=True)
|
teosd_process = Process(target=main, kwargs={"command_line_conf": {}}, daemon=True)
|
||||||
teosd_process.start()
|
teosd_process.start()
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import json
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from riemann.tx import Tx
|
from riemann.tx import Tx
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
@@ -27,6 +28,7 @@ common.cryptographer.logger = Logger(actor="Cryptographer", log_name_prefix="")
|
|||||||
teos_base_endpoint = "http://{}:{}".format(cli_config.get("TEOS_SERVER"), cli_config.get("TEOS_PORT"))
|
teos_base_endpoint = "http://{}:{}".format(cli_config.get("TEOS_SERVER"), cli_config.get("TEOS_PORT"))
|
||||||
teos_add_appointment_endpoint = "{}/add_appointment".format(teos_base_endpoint)
|
teos_add_appointment_endpoint = "{}/add_appointment".format(teos_base_endpoint)
|
||||||
teos_get_appointment_endpoint = "{}/get_appointment".format(teos_base_endpoint)
|
teos_get_appointment_endpoint = "{}/get_appointment".format(teos_base_endpoint)
|
||||||
|
teos_get_all_appointments_endpoint = "{}/get_all_appointments".format(teos_base_endpoint)
|
||||||
|
|
||||||
# Run teosd
|
# Run teosd
|
||||||
teosd_process = run_teosd()
|
teosd_process = run_teosd()
|
||||||
@@ -53,6 +55,11 @@ def add_appointment(appointment_data, sk=cli_sk):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_appointments():
|
||||||
|
r = teos_cli.get_all_appointments(teos_base_endpoint)
|
||||||
|
return json.loads(r)
|
||||||
|
|
||||||
|
|
||||||
def test_commands_non_registered(bitcoin_cli, create_txs):
|
def test_commands_non_registered(bitcoin_cli, create_txs):
|
||||||
# All commands should fail if the user is not registered
|
# All commands should fail if the user is not registered
|
||||||
|
|
||||||
@@ -101,6 +108,11 @@ def test_appointment_life_cycle(bitcoin_cli, create_txs):
|
|||||||
assert appointment_info is not None
|
assert appointment_info is not None
|
||||||
assert appointment_info.get("status") == "being_watched"
|
assert appointment_info.get("status") == "being_watched"
|
||||||
|
|
||||||
|
all_appointments = get_all_appointments()
|
||||||
|
watching = all_appointments.get("watcher_appointments")
|
||||||
|
responding = all_appointments.get("responder_trackers")
|
||||||
|
assert len(watching) == 1 and len(responding) == 0
|
||||||
|
|
||||||
new_addr = bitcoin_cli.getnewaddress()
|
new_addr = bitcoin_cli.getnewaddress()
|
||||||
broadcast_transaction_and_mine_block(bitcoin_cli, commitment_tx, new_addr)
|
broadcast_transaction_and_mine_block(bitcoin_cli, commitment_tx, new_addr)
|
||||||
|
|
||||||
@@ -108,6 +120,11 @@ def test_appointment_life_cycle(bitcoin_cli, create_txs):
|
|||||||
assert appointment_info is not None
|
assert appointment_info is not None
|
||||||
assert appointment_info.get("status") == "dispute_responded"
|
assert appointment_info.get("status") == "dispute_responded"
|
||||||
|
|
||||||
|
all_appointments = get_all_appointments()
|
||||||
|
watching = all_appointments.get("watcher_appointments")
|
||||||
|
responding = all_appointments.get("responder_trackers")
|
||||||
|
assert len(watching) == 0 and len(responding) == 1
|
||||||
|
|
||||||
# It can be also checked by ensuring that the penalty transaction made it to the network
|
# It can be also checked by ensuring that the penalty transaction made it to the network
|
||||||
penalty_tx_id = bitcoin_cli.decoderawtransaction(penalty_tx).get("txid")
|
penalty_tx_id = bitcoin_cli.decoderawtransaction(penalty_tx).get("txid")
|
||||||
|
|
||||||
@@ -129,6 +146,52 @@ def test_appointment_life_cycle(bitcoin_cli, create_txs):
|
|||||||
assert get_appointment_info(locator) is None
|
assert get_appointment_info(locator) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_multiple_appointments_life_cycle(bitcoin_cli, create_five_txs):
|
||||||
|
# Tests that get_all_appointments returns all the appointments the tower is storing at various stages in the appointment lifecycle.
|
||||||
|
appointments = []
|
||||||
|
|
||||||
|
commitment_txs, penalty_txs = create_five_txs
|
||||||
|
|
||||||
|
# Create five appointments.
|
||||||
|
for i in range(5):
|
||||||
|
appointment = {}
|
||||||
|
|
||||||
|
appointment["commitment_tx"] = commitment_txs[i]
|
||||||
|
appointment["penalty_tx"] = penalty_txs[i]
|
||||||
|
commitment_tx_id = bitcoin_cli.decoderawtransaction(commitment_txs[i]).get("txid")
|
||||||
|
appointment_data = build_appointment_data(bitcoin_cli, commitment_tx_id, penalty_txs[i])
|
||||||
|
appointment["appointment_data"] = appointment_data
|
||||||
|
locator = compute_locator(commitment_tx_id)
|
||||||
|
appointment["locator"] = locator
|
||||||
|
|
||||||
|
appointments.append(appointment)
|
||||||
|
|
||||||
|
# Send all of them to watchtower.
|
||||||
|
for appt in appointments:
|
||||||
|
add_appointment(appt.get("appointment_data"))
|
||||||
|
|
||||||
|
# Two of these appointments are breached, and the watchtower responds to them.
|
||||||
|
for i in range(2):
|
||||||
|
new_addr = bitcoin_cli.getnewaddress()
|
||||||
|
broadcast_transaction_and_mine_block(bitcoin_cli, appointments[i]["commitment_tx"], new_addr)
|
||||||
|
bitcoin_cli.generatetoaddress(3, new_addr)
|
||||||
|
sleep(1)
|
||||||
|
|
||||||
|
# Test that they all show up in get_all_appointments at the correct stages.
|
||||||
|
all_appointments = get_all_appointments()
|
||||||
|
watching = all_appointments.get("watcher_appointments")
|
||||||
|
responding = all_appointments.get("responder_trackers")
|
||||||
|
assert len(watching) == 3 and len(responding) == 2
|
||||||
|
|
||||||
|
# Now let's mine some blocks so these appointments reach the end of their lifecycle.
|
||||||
|
# Since we are running all the nodes remotely data may take more time than normal, and some confirmations may be
|
||||||
|
# missed, so we generate more than enough confirmations and add some delays.
|
||||||
|
new_addr = bitcoin_cli.getnewaddress()
|
||||||
|
for _ in range(int(1.5 * END_TIME_DELTA) + 5):
|
||||||
|
sleep(1)
|
||||||
|
bitcoin_cli.generatetoaddress(1, new_addr)
|
||||||
|
|
||||||
|
|
||||||
def test_appointment_malformed_penalty(bitcoin_cli, create_txs):
|
def test_appointment_malformed_penalty(bitcoin_cli, create_txs):
|
||||||
# Lets start by creating two valid transaction
|
# Lets start by creating two valid transaction
|
||||||
commitment_tx, penalty_tx = create_txs
|
commitment_tx, penalty_tx = create_txs
|
||||||
|
|||||||
Reference in New Issue
Block a user