From c0a9dfb84d292cd0ea43d6784596df49efb4fdd3 Mon Sep 17 00:00:00 2001 From: saratomaz Date: Mon, 5 Dec 2022 19:37:17 +0000 Subject: [PATCH] check reference inputs txins on dbsync --- cardano_node_tests/tests/test_scripts.py | 164 ++++++++++++++------- cardano_node_tests/utils/dbsync_queries.py | 4 +- cardano_node_tests/utils/dbsync_utils.py | 40 +++++ 3 files changed, 152 insertions(+), 56 deletions(-) diff --git a/cardano_node_tests/tests/test_scripts.py b/cardano_node_tests/tests/test_scripts.py index bcadce6a5..287b0ecee 100644 --- a/cardano_node_tests/tests/test_scripts.py +++ b/cardano_node_tests/tests/test_scripts.py @@ -9,11 +9,14 @@ import logging import random from pathlib import Path +from typing import Any +from typing import Dict from typing import List from typing import Optional import allure import pytest +from _pytest.fixtures import FixtureRequest from cardano_clusterlib import clusterlib from cardano_node_tests.cluster_management import cluster_management @@ -1914,69 +1917,115 @@ def payment_addrs( return addrs - @allure.link(helpers.get_vcs_link()) - @common.PARAM_USE_BUILD_CMD - @pytest.mark.parametrize("script_version", ("simple_v1", "simple_v2")) - @pytest.mark.dbsync - def test_script_reference_utxo( + @pytest.fixture + def create_reference_script( self, + cluster_manager: cluster_management.ClusterManager, cluster: clusterlib.ClusterLib, payment_addrs: List[clusterlib.AddressRecord], - use_build_cmd: bool, - script_version: str, - ): - """Send funds from script address where script is on reference UTxO.""" - temp_template = f"{common.get_test_id(cluster)}_{script_version}_{use_build_cmd}" - src_addr = payment_addrs[0] - dst_addr = payment_addrs[1] + ) -> Dict[str, Any]: + with cluster_manager.cache_fixture() as fixture_cache: + if fixture_cache.value: + return fixture_cache.value # type: ignore - fund_amount = 4_500_000 - amount = 2_000_000 + temp_template = common.get_test_id(cluster) - # create multisig script - if script_version == "simple_v1": - invalid_before = None - invalid_hereafter = None + src_addr = payment_addrs[0] + dst_addr = payment_addrs[1] - reference_type = clusterlib.ScriptTypes.SIMPLE_V1 - script_type_str = "SimpleScriptV1" + # create multisig script v1 - multisig_script = Path(f"{temp_template}_multisig.script") - script_content = { + multisig_script_v1 = Path(f"{temp_template}_multisig_v1.script") + script_content_v1 = { "keyHash": cluster.g_address.get_payment_vkey_hash(dst_addr.vkey_file), "type": "sig", } - with open(multisig_script, "w", encoding="utf-8") as fp_out: - json.dump(script_content, fp_out, indent=4) - else: - invalid_before = 100 - invalid_hereafter = cluster.g_query.get_slot_no() + 1_000 + with open(multisig_script_v1, "w", encoding="utf-8") as fp_out: + json.dump(script_content_v1, fp_out, indent=4) + + reference_utxo_v1, tx_out_reference_v1 = clusterlib_utils.create_reference_utxo( + temp_template=f"{temp_template}_v1", + cluster_obj=cluster, + payment_addr=src_addr, + dst_addr=dst_addr, + script_file=multisig_script_v1, + amount=4_000_000, + ) + assert reference_utxo_v1.reference_script - reference_type = clusterlib.ScriptTypes.SIMPLE_V2 - script_type_str = "SimpleScriptV2" + # create multisig script v2 - multisig_script = cluster.g_transaction.build_multisig_script( - script_name=temp_template, + multisig_script_v2 = cluster.g_transaction.build_multisig_script( + script_name=f"{temp_template}_v2", script_type_arg=clusterlib.MultiSigTypeArgs.ANY, payment_vkey_files=[p.vkey_file for p in payment_addrs], - slot=invalid_before, + slot=cluster.g_query.get_slot_no() + 1_000, slot_type_arg=clusterlib.MultiSlotTypeArgs.AFTER, ) - # create reference UTxO - reference_utxo, tx_out_reference = clusterlib_utils.create_reference_utxo( - temp_template=temp_template, - cluster_obj=cluster, - payment_addr=src_addr, - dst_addr=dst_addr, - script_file=multisig_script, - amount=4_000_000, - ) - assert reference_utxo.reference_script + reference_utxo_v2, tx_out_reference_v2 = clusterlib_utils.create_reference_utxo( + temp_template=f"{temp_template}_v2", + cluster_obj=cluster, + payment_addr=src_addr, + dst_addr=dst_addr, + script_file=multisig_script_v2, + amount=4_000_000, + ) + assert reference_utxo_v2.reference_script + + scripts_data = { + "simple_v1": { + "invalid_before": None, + "invalid_hereafter": None, + "reference_type": clusterlib.ScriptTypes.SIMPLE_V1, + "script_type_str": "SimpleScriptV1", + "multisig_script": multisig_script_v1, + "reference_utxo": reference_utxo_v1, + "tx_out_reference": tx_out_reference_v1, + }, + "simple_v2": { + "invalid_before": 100, + "invalid_hereafter": cluster.g_query.get_slot_no() + 1_000, + "reference_type": clusterlib.ScriptTypes.SIMPLE_V2, + "script_type_str": "SimpleScriptV2", + "multisig_script": multisig_script_v2, + "reference_utxo": reference_utxo_v2, + "tx_out_reference": tx_out_reference_v2, + }, + } + + fixture_cache.value = scripts_data + + return scripts_data + + @allure.link(helpers.get_vcs_link()) + @common.PARAM_USE_BUILD_CMD + @pytest.mark.parametrize("script_version", ("simple_v1", "simple_v2")) + @pytest.mark.dbsync + def test_script_reference_utxo( + self, + cluster: clusterlib.ClusterLib, + payment_addrs: List[clusterlib.AddressRecord], + create_reference_script: Dict[str, Any], + script_version: str, + use_build_cmd: bool, + request: FixtureRequest, + ): + """Send funds from script address where script is on reference UTxO.""" + temp_template = f"{common.get_test_id(cluster)}_{request.node.callspec.id}" + src_addr = payment_addrs[0] + dst_addr = payment_addrs[1] + + fund_amount = 4_500_000 + amount = 2_000_000 + + # create reference UTxO with the script + scripts_data_by_version = create_reference_script + script_data = scripts_data_by_version[script_version] # create script address script_address = cluster.g_address.gen_payment_addr( - addr_name=temp_template, payment_script_file=multisig_script + addr_name=temp_template, payment_script_file=script_data["multisig_script"] ) # send funds to script address @@ -1999,8 +2048,8 @@ def test_script_reference_utxo( script_txins = [ clusterlib.ScriptTxIn( txins=[], - reference_txin=reference_utxo, - reference_type=reference_type, + reference_txin=script_data["reference_utxo"], + reference_type=script_data["reference_type"], ) ] @@ -2012,8 +2061,8 @@ def test_script_reference_utxo( script_txins=script_txins, fee_buffer=2_000_000, tx_files=tx_files, - invalid_hereafter=invalid_hereafter, - invalid_before=invalid_before, + invalid_hereafter=script_data["invalid_hereafter"], + invalid_before=script_data["invalid_before"], witness_override=2, ) tx_signed = cluster.g_transaction.sign_tx( @@ -2029,8 +2078,8 @@ def test_script_reference_utxo( txouts=destinations, script_txins=script_txins, tx_files=tx_files, - invalid_hereafter=invalid_hereafter, - invalid_before=invalid_before, + invalid_hereafter=script_data["invalid_hereafter"], + invalid_before=script_data["invalid_before"], ) # check final balances @@ -2044,20 +2093,25 @@ def test_script_reference_utxo( ), f"Incorrect balance for destination address `{dst_addr.address}`" # check that reference UTxO was NOT spent - assert cluster.g_query.get_utxo(utxo=reference_utxo), "Reference input was spent" + assert cluster.g_query.get_utxo( + utxo=script_data["reference_utxo"] + ), "Reference input was spent" - dbsync_utils.check_tx(cluster_obj=cluster, tx_raw_output=tx_out_reference) + dbsync_utils.check_tx(cluster_obj=cluster, tx_raw_output=script_data["tx_out_reference"]) dbsync_utils.check_tx(cluster_obj=cluster, tx_raw_output=tx_out_to) - # TODO: check reference script in db-sync (the `tx_out_from`) + dbsync_utils.check_tx(cluster_obj=cluster, tx_raw_output=tx_out_from) # check expected script type # TODO: moved the check to the end of the test because of XFAIL if ( - script_type_str == "SimpleScriptV1" - and reference_utxo.reference_script["script"]["type"] == "SimpleScriptV2" + script_data["script_type_str"] == "SimpleScriptV1" + and script_data["reference_utxo"].reference_script["script"]["type"] == "SimpleScriptV2" ): pytest.xfail("Reported 'SimpleScriptV2', see node issue #4261") - assert reference_utxo.reference_script["script"]["type"] == script_type_str + assert ( + script_data["reference_utxo"].reference_script["script"]["type"] + == script_data["script_type_str"] + ) @allure.link(helpers.get_vcs_link()) @common.PARAM_USE_BUILD_CMD diff --git a/cardano_node_tests/utils/dbsync_queries.py b/cardano_node_tests/utils/dbsync_queries.py index f5cc5ee49..c742d12ad 100644 --- a/cardano_node_tests/utils/dbsync_queries.py +++ b/cardano_node_tests/utils/dbsync_queries.py @@ -62,6 +62,8 @@ class TxDBRow(NamedTuple): tx_out_data_hash: Optional[memoryview] tx_out_inline_datum_hash: Optional[memoryview] tx_out_reference_script_hash: Optional[memoryview] + tx_out_reference_script_tx_id: Optional[int] + tx_out_reference_script_id: Optional[int] metadata_count: int reserve_count: int treasury_count: int @@ -284,7 +286,7 @@ def query_tx(txhash: str) -> Generator[TxDBRow, None, None]: " tx.id, tx.hash, tx.block_id, tx.block_index, tx.out_sum, tx.fee, tx.deposit, tx.size," " tx.invalid_before, tx.invalid_hereafter," " tx_out.id, tx_out.tx_id, tx_out.index, tx_out.address, tx_out.address_has_script," - " tx_out.value, tx_out.data_hash, datum.hash, script.hash," + " tx_out.value, tx_out.data_hash, datum.hash, script.hash, script.tx_id, script.id," " (SELECT COUNT(id) FROM tx_metadata WHERE tx_id=tx.id) AS metadata_count," " (SELECT COUNT(id) FROM reserve WHERE tx_id=tx.id) AS reserve_count," " (SELECT COUNT(id) FROM treasury WHERE tx_id=tx.id) AS treasury_count," diff --git a/cardano_node_tests/utils/dbsync_utils.py b/cardano_node_tests/utils/dbsync_utils.py index 9d4a49445..3886e80b1 100644 --- a/cardano_node_tests/utils/dbsync_utils.py +++ b/cardano_node_tests/utils/dbsync_utils.py @@ -1,8 +1,10 @@ """Functionality for interacting with db-sync.""" import functools import itertools +import json import logging import time +from pathlib import Path from typing import Any from typing import Dict from typing import List @@ -15,6 +17,7 @@ from cardano_node_tests.utils import clusterlib_utils from cardano_node_tests.utils import configuration from cardano_node_tests.utils import dbsync_queries +from cardano_node_tests.utils import helpers LOGGER = logging.getLogger(__name__) @@ -143,6 +146,7 @@ class TxRecord(NamedTuple): collateral_outputs: List[clusterlib.UTXOData] reference_inputs: List[clusterlib.UTXOData] scripts: List[ScriptRecord] + script_txins: List[ScriptRecord] redeemers: List[RedeemerRecord] metadata: List[MetadataRecord] reserve: List[ADAStashRecord] @@ -544,6 +548,18 @@ def get_tx_record(txhash: str) -> TxRecord: # noqa: C901 for r in dbsync_queries.query_plutus_scripts(txhash=txhash) ] + script_txins = [] + if reference_inputs: + for reference_input in reference_inputs: + script_txins += [ + ScriptRecord( + hash=r.hash.hex(), + type=str(r.type), + serialised_size=int(r.serialised_size) if r.serialised_size else 0, + ) + for r in dbsync_queries.query_plutus_scripts(txhash=reference_input.utxo_hash) + ] + redeemers = [] if txdata.last_row.redeemer_count: redeemers = [ @@ -579,6 +595,7 @@ def get_tx_record(txhash: str) -> TxRecord: # noqa: C901 collateral_outputs=collateral_outputs, reference_inputs=reference_inputs, scripts=scripts, + script_txins=script_txins, redeemers=redeemers, metadata=metadata, reserve=reserve, @@ -956,6 +973,29 @@ def check_tx( f"({tx_reference_script_hashes} != {db_reference_script_hashes})" ) + # check reference scripts txins + txins_script_hashes = [] + + for r in tx_raw_output.script_txins: + if r.reference_txin and r.reference_txin.reference_script: + script_file = Path(f"{helpers.get_timestamped_rand_str()}.script") + with open(script_file, "w", encoding="utf-8") as outfile: + json.dump(r.reference_txin.reference_script["script"], outfile) + + txins_script_hashes.append( + cluster_obj.g_transaction.get_policyid(script_file=script_file) + ) + + db_txins_script_hashes = {r.hash for r in response.script_txins if r.hash} + + # a script is added to `script` table only the first time it is seen, so the record + # can be empty for the current transaction + if db_txins_script_hashes: + assert set(txins_script_hashes) == db_txins_script_hashes, ( + "Reference scripts txins don't match " + f"({set(txins_script_hashes)} != {db_txins_script_hashes})" + ) + return response