From f91475c61bf907006e7351163c98379f52d836fc Mon Sep 17 00:00:00 2001 From: Sergi Delgado Segura Date: Fri, 17 Jan 2020 17:51:09 +0100 Subject: [PATCH] Updates life cycle tests and adds malformed tx tests --- test/pisa/e2e/test_basic_e2e.py | 150 ++++++++++++++------------------ 1 file changed, 65 insertions(+), 85 deletions(-) diff --git a/test/pisa/e2e/test_basic_e2e.py b/test/pisa/e2e/test_basic_e2e.py index b635ea6..1fddef0 100644 --- a/test/pisa/e2e/test_basic_e2e.py +++ b/test/pisa/e2e/test_basic_e2e.py @@ -1,105 +1,58 @@ import json from time import sleep -from decimal import Decimal, getcontext +from riemann.tx import Tx -import pisa.conf as conf from pisa import HOST, PORT -from pisa.utils.auth_proxy import AuthServiceProxy - -from common.tools import compute_locator - from apps.cli import pisa_cli +from pisa.utils.auth_proxy import JSONRPCException +from common.tools import compute_locator +from test.pisa.e2e.conftest import END_TIME_DELTA, build_appointment_data + +# We'll use pisa_cli to add appointments. The expected input format is a list of arguments with a json-encoded +# appointment +pisa_cli.pisa_api_server = HOST +pisa_cli.pisa_api_port = PORT -getcontext().prec = 10 - -bitcoin_cli = AuthServiceProxy( - "http://%s:%s@%s:%d" % (conf.BTC_RPC_USER, conf.BTC_RPC_PASSWD, conf.BTC_RPC_HOST, 18444) -) - -END_TIME_DELTA = 10 - - -def create_txs(): - utxos = bitcoin_cli.listunspent() - - if len(utxos) == 0: - raise ValueError("There's no UTXOs.") - - commitment_tx_ins = {"txid": utxos[0].get("txid"), "vout": utxos[0].get("vout")} - commitment_tx_outs = {utxos[0].get("address"): utxos[0].get("amount") - Decimal(1 / pow(10, 5))} - - raw_commitment_tx = bitcoin_cli.createrawtransaction([commitment_tx_ins], commitment_tx_outs) - signed_commitment_tx = bitcoin_cli.signrawtransactionwithwallet(raw_commitment_tx) - - if not signed_commitment_tx.get("complete"): - raise ValueError("Couldn't sign transaction. {}".format(signed_commitment_tx)) - - decoded_commitment_tx = bitcoin_cli.decoderawtransaction(signed_commitment_tx.get("hex")) - - penalty_tx_ins = {"txid": decoded_commitment_tx.get("txid"), "vout": 0} - address = decoded_commitment_tx.get("vout")[0].get("scriptPubKey").get("addresses")[0] - penalty_tx_outs = {address: decoded_commitment_tx.get("vout")[0].get("value") - Decimal(1 / pow(10, 5))} - - orphan_info = { - "txid": decoded_commitment_tx.get("txid"), - "scriptPubKey": decoded_commitment_tx.get("vout")[0].get("scriptPubKey").get("hex"), - "vout": 0, - "amount": decoded_commitment_tx.get("vout")[0].get("value"), - } - - raw_penalty_tx = bitcoin_cli.createrawtransaction([penalty_tx_ins], penalty_tx_outs) - signed_penalty_tx = bitcoin_cli.signrawtransactionwithwallet(raw_penalty_tx, [orphan_info]) - - if not signed_penalty_tx.get("complete"): - raise ValueError("Couldn't sign orphan transaction. {}".format(signed_commitment_tx)) - - return signed_commitment_tx.get("hex"), signed_penalty_tx.get("hex") - - -def build_appointment_data(commitment_tx, penalty_tx): - commitment_tx_id = bitcoin_cli.decoderawtransaction(commitment_tx).get("txid") - current_height = bitcoin_cli.getblockcount() - - appointment_data = { - "tx": penalty_tx, - "tx_id": commitment_tx_id, - "start_time": current_height + 1, - "end_time": current_height + 1 + END_TIME_DELTA, - "to_self_delay": 20, - } - - return appointment_data - - -def test_appointment_life_cycle(): - commitment_tx, penalty_tx = create_txs() - appointment_data = build_appointment_data(commitment_tx, penalty_tx) - - # We'll use pisa_cli to add the appointment. The expected input format is a list of arguments with a json-encoded - # appointment - pisa_cli.pisa_api_server = HOST - pisa_cli.pisa_api_port = PORT - - response = pisa_cli.add_appointment([json.dumps(appointment_data)]) - assert response is True - +def broadcast_transaction_and_mine_block(bitcoin_cli, commitment_tx, addr): # Broadcast the commitment transaction and mine a block - new_addr = bitcoin_cli.getnewaddress() bitcoin_cli.sendrawtransaction(commitment_tx) - bitcoin_cli.generatetoaddress(1, new_addr) + bitcoin_cli.generatetoaddress(1, addr) + +def get_appointment_info(locator): # Check that the justice has been triggered (the appointment has moved from Watcher to Responder) + sleep(1) # Let's add a bit of delay so the state can be updated + return pisa_cli.get_appointment([locator]) + + +def test_appointment_life_cycle(bitcoin_cli, create_txs): + commitment_tx, penalty_tx = create_txs + appointment_data = build_appointment_data(bitcoin_cli, commitment_tx, penalty_tx) locator = compute_locator(appointment_data.get("tx_id")) - # Let's add a bit of delay so the state can be updated - sleep(1) - appointment_info = pisa_cli.get_appointment([locator]) + assert pisa_cli.add_appointment([json.dumps(appointment_data)]) is True + + new_addr = bitcoin_cli.getnewaddress() + broadcast_transaction_and_mine_block(bitcoin_cli, commitment_tx, new_addr) + + appointment_info = get_appointment_info(locator) assert appointment_info is not None assert len(appointment_info) == 1 assert appointment_info[0].get("status") == "dispute_responded" + # 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") + + try: + bitcoin_cli.getrawtransaction(penalty_tx_id) + assert True + + except JSONRPCException: + # If the transaction if not found. + assert False + # Now let's mine some blocks so the appointment reaches its end. # 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. @@ -107,5 +60,32 @@ def test_appointment_life_cycle(): sleep(1) bitcoin_cli.generatetoaddress(1, new_addr) - appointment_info = pisa_cli.get_appointment([locator]) + appointment_info = get_appointment_info(locator) + assert appointment_info[0].get("status") == "not_found" + + +def test_appointment_malformed_penalty(bitcoin_cli, create_txs): + # Lets start by creating two valid transaction + commitment_tx, penalty_tx = create_txs + + # Now we can modify the penalty so it is invalid when broadcast + mod_penalty_tx = Tx.from_hex(penalty_tx) + tx_in = mod_penalty_tx.tx_ins[0].copy(redeem_script=b"") + mod_penalty_tx = mod_penalty_tx.copy(tx_ins=[tx_in]) + + appointment_data = build_appointment_data(bitcoin_cli, commitment_tx, mod_penalty_tx.hex()) + locator = compute_locator(appointment_data.get("tx_id")) + + assert pisa_cli.add_appointment([json.dumps(appointment_data)]) is True + + # Broadcast the commitment transaction and mine a block + new_addr = bitcoin_cli.getnewaddress() + broadcast_transaction_and_mine_block(bitcoin_cli, commitment_tx, new_addr) + + # The appointment should have been removed since the penalty_tx was malformed. + sleep(1) + appointment_info = get_appointment_info(locator) + + assert appointment_info is not None + assert len(appointment_info) == 1 assert appointment_info[0].get("status") == "not_found"