def test_delegate_using_vkey(
        self,
        cluster_manager: cluster_management.ClusterManager,
        cluster_use_pool1: clusterlib.ClusterLib,
        use_build_cmd: bool,
    ):
        """Submit registration certificate and delegate to pool using cold vkey.

        * register stake address and delegate it to pool
        * check that the stake address was delegated
        * (optional) check records in db-sync
        """
        pool_name = cluster_management.Resources.POOL1
        cluster = cluster_use_pool1
        temp_template = f"{common.get_test_id(cluster)}_{use_build_cmd}"

        clusterlib_utils.wait_for_epoch_interval(cluster_obj=cluster,
                                                 start=5,
                                                 stop=-20)
        init_epoch = cluster.get_epoch()

        # submit registration certificate and delegate to pool
        node_cold = cluster_manager.cache.addrs_data[pool_name][
            "cold_key_pair"]
        delegation_out = delegation.delegate_stake_addr(
            cluster_obj=cluster,
            addrs_data=cluster_manager.cache.addrs_data,
            temp_template=temp_template,
            cold_vkey=node_cold.vkey_file,
            use_build_cmd=use_build_cmd,
        )

        tx_db_record = dbsync_utils.check_tx(
            cluster_obj=cluster, tx_raw_output=delegation_out.tx_raw_output)
        delegation.db_check_delegation(
            pool_user=delegation_out.pool_user,
            db_record=tx_db_record,
            deleg_epoch=init_epoch,
            pool_id=delegation_out.pool_id,
        )
    def test_delegate_using_pool_id(
        self,
        cluster_manager: cluster_management.ClusterManager,
        cluster_and_pool: Tuple[clusterlib.ClusterLib, str],
        use_build_cmd: bool,
    ):
        """Submit registration certificate and delegate to pool using pool id.

        * register stake address and delegate it to pool
        * check that the stake address was delegated
        * (optional) check records in db-sync
        """
        cluster, pool_id = cluster_and_pool
        temp_template = f"{common.get_test_id(cluster)}_{use_build_cmd}"

        clusterlib_utils.wait_for_epoch_interval(cluster_obj=cluster,
                                                 start=5,
                                                 stop=-20)
        init_epoch = cluster.get_epoch()

        # submit registration certificate and delegate to pool
        delegation_out = delegation.delegate_stake_addr(
            cluster_obj=cluster,
            addrs_data=cluster_manager.cache.addrs_data,
            temp_template=temp_template,
            pool_id=pool_id,
            use_build_cmd=use_build_cmd,
        )

        tx_db_record = dbsync_utils.check_tx(
            cluster_obj=cluster, tx_raw_output=delegation_out.tx_raw_output)
        delegation.db_check_delegation(
            pool_user=delegation_out.pool_user,
            db_record=tx_db_record,
            deleg_epoch=init_epoch,
            pool_id=delegation_out.pool_id,
        )
Exemple #3
0
    def test_build_pay_stake_addr_from(
        self,
        cluster_pots: clusterlib.ClusterLib,
        registered_user: clusterlib.PoolUser,
        fund_src: str,
    ):
        """Send funds from the reserves or treasury pot to stake address.

        Uses `cardano-cli transaction build` command for building the transactions.

        * generate an MIR certificate
        * submit a TX with the MIR certificate
        * check that the expected amount was added to the stake address reward account
        """
        temp_template = helpers.get_func_name()
        cluster = cluster_pots
        amount = 50_000_000

        init_reward = cluster.get_stake_addr_info(
            registered_user.stake.address
        ).reward_account_balance
        init_balance = cluster.get_address_balance(registered_user.payment.address)

        mir_cert = cluster.gen_mir_cert_stake_addr(
            stake_addr=registered_user.stake.address,
            reward=amount,
            tx_name=temp_template,
            use_treasury=fund_src == self.TREASURY,
        )
        tx_files = clusterlib.TxFiles(
            certificate_files=[mir_cert],
            signing_key_files=[
                registered_user.payment.skey_file,
                *cluster.genesis_keys.delegate_skeys,
            ],
        )

        # send the transaction at the beginning of an epoch
        if cluster.time_from_epoch_start() > (cluster.epoch_length_sec // 6):
            cluster.wait_for_new_epoch()

        tx_output = cluster.build_tx(
            src_address=registered_user.payment.address,
            tx_name=temp_template,
            tx_files=tx_files,
            fee_buffer=1000_000,
            witness_override=2,
        )
        tx_signed = cluster.sign_tx(
            tx_body_file=tx_output.out_file,
            signing_key_files=tx_files.signing_key_files,
            tx_name=temp_template,
        )
        cluster.submit_tx(tx_file=tx_signed, txins=tx_output.txins)

        assert (
            cluster.get_address_balance(registered_user.payment.address) < init_balance
        ), f"Incorrect balance for source address `{registered_user.payment.address}`"

        cluster.wait_for_new_epoch()

        assert (
            cluster.get_stake_addr_info(registered_user.stake.address).reward_account_balance
            == init_reward + amount
        ), f"Incorrect reward balance for stake address `{registered_user.stake.address}`"

        tx_db_record = dbsync_utils.check_tx(cluster_obj=cluster, tx_raw_output=tx_output)
        if tx_db_record:
            stash_record = (
                tx_db_record.treasury[0] if fund_src == self.TREASURY else tx_db_record.reserve[0]
            )
            assert stash_record.amount == amount, (
                "Incorrect amount transferred using MIR certificate "
                f"({stash_record.amount} != {amount})"
            )
            assert stash_record.address == registered_user.stake.address, (
                "Incorrect stake address "
                f"({stash_record.address} != {registered_user.stake.address})"
            )
Exemple #4
0
    def test_build_transfer_to_reserves(
        self, cluster_pots: clusterlib.ClusterLib, pool_users: List[clusterlib.PoolUser]
    ):
        """Send funds from the treasury pot to the reserves pot.

        Uses `cardano-cli transaction build` command for building the transactions.
        """
        temp_template = helpers.get_func_name()
        cluster = cluster_pots
        pool_user = pool_users[0]
        amount = 1_000_000_000_000

        init_balance = cluster.get_address_balance(pool_user.payment.address)

        mir_cert = cluster.gen_mir_cert_to_rewards(transfer=amount, tx_name=temp_template)
        tx_files = clusterlib.TxFiles(
            certificate_files=[mir_cert],
            signing_key_files=[pool_user.payment.skey_file, *cluster.genesis_keys.delegate_skeys],
        )

        # send the transaction at the beginning of an epoch
        if cluster.time_from_epoch_start() > (cluster.epoch_length_sec // 6):
            cluster.wait_for_new_epoch()

        tx_output = cluster.build_tx(
            src_address=pool_user.payment.address,
            tx_name=temp_template,
            tx_files=tx_files,
            fee_buffer=1000_000,
            witness_override=2,
        )
        tx_signed = cluster.sign_tx(
            tx_body_file=tx_output.out_file,
            signing_key_files=tx_files.signing_key_files,
            tx_name=temp_template,
        )
        cluster.submit_tx(tx_file=tx_signed, txins=tx_output.txins)

        assert (
            cluster.get_address_balance(pool_user.payment.address) < init_balance
        ), f"Incorrect balance for source address `{pool_user.payment.address}`"

        tx_db_record = dbsync_utils.check_tx(cluster_obj=cluster, tx_raw_output=tx_output)
        if tx_db_record:
            tx_epoch = cluster.get_epoch()

            assert tx_db_record.pot_transfers[0].treasury == -amount, (
                "Incorrect amount transferred from treasury "
                f"({tx_db_record.pot_transfers[0].treasury} != {-amount})"
            )
            assert tx_db_record.pot_transfers[0].reserves == amount, (
                "Incorrect amount transferred to reserves "
                f"({tx_db_record.pot_transfers[0].reserves} != {amount})"
            )

            cluster.wait_for_new_epoch()

            pots_records = list(dbsync_utils.query_ada_pots(epoch_from=tx_epoch))
            # normally `treasury[-1]` > `treasury[-2]`
            assert pots_records[-1].treasury < pots_records[-2].treasury
            # normally `reserves[-1]` < `reserves[-2]`
            assert pots_records[-1].reserves > pots_records[-2].reserves
    def test_minting_one_token(self, cluster: clusterlib.ClusterLib,
                               payment_addrs: List[clusterlib.AddressRecord]):
        """Test minting a token with a Plutus script.

        Uses `cardano-cli transaction build` command for building the transactions.

        * fund the token issuer and create a UTxO for collateral
        * check that the expected amount was transferred to token issuer's address
        * mint the token using a Plutus script
        * check that the token was minted and collateral UTxO was not spent
        * check expected fees
        * check expected Plutus cost
        * (optional) check transactions in db-sync
        """
        # pylint: disable=too-many-locals
        temp_template = common.get_test_id(cluster)
        payment_addr = payment_addrs[0]
        issuer_addr = payment_addrs[1]

        lovelace_amount = 2_000_000
        token_amount = 5
        script_fund = 200_000_000

        minting_cost = plutus_common.compute_cost(
            execution_cost=plutus_common.MINTING_COST,
            protocol_params=cluster.get_protocol_params())

        issuer_init_balance = cluster.get_address_balance(issuer_addr.address)

        # Step 1: fund the token issuer and create UTXO for collaterals

        mint_utxos, collateral_utxos, tx_output_step1 = _fund_issuer(
            cluster_obj=cluster,
            temp_template=temp_template,
            payment_addr=payment_addr,
            issuer_addr=issuer_addr,
            minting_cost=minting_cost,
            amount=script_fund,
            collateral_utxo_num=2,
        )

        # Step 2: mint the "qacoin"

        policyid = cluster.get_policyid(plutus_common.MINTING_PLUTUS_V1)
        asset_name = f"qacoin{clusterlib.get_rand_str(4)}".encode(
            "utf-8").hex()
        token = f"{policyid}.{asset_name}"
        mint_txouts = [
            clusterlib.TxOut(address=issuer_addr.address,
                             amount=token_amount,
                             coin=token)
        ]

        plutus_mint_data = [
            clusterlib.Mint(
                txouts=mint_txouts,
                script_file=plutus_common.MINTING_PLUTUS_V1,
                collaterals=collateral_utxos,
                redeemer_file=plutus_common.REDEEMER_42,
            )
        ]

        tx_files_step2 = clusterlib.TxFiles(
            signing_key_files=[issuer_addr.skey_file], )
        txouts_step2 = [
            clusterlib.TxOut(address=issuer_addr.address,
                             amount=lovelace_amount),
            *mint_txouts,
        ]
        tx_output_step2 = cluster.build_tx(
            src_address=payment_addr.address,
            tx_name=f"{temp_template}_step2",
            tx_files=tx_files_step2,
            txins=mint_utxos,
            txouts=txouts_step2,
            mint=plutus_mint_data,
        )
        plutus_cost = cluster.calculate_plutus_script_cost(
            src_address=payment_addr.address,
            tx_name=f"{temp_template}_step2",
            tx_files=tx_files_step2,
            txins=mint_utxos,
            txouts=txouts_step2,
            mint=plutus_mint_data,
        )
        tx_signed_step2 = cluster.sign_tx(
            tx_body_file=tx_output_step2.out_file,
            signing_key_files=tx_files_step2.signing_key_files,
            tx_name=f"{temp_template}_step2",
        )
        cluster.submit_tx(tx_file=tx_signed_step2, txins=mint_utxos)

        assert (
            cluster.get_address_balance(
                issuer_addr.address) == issuer_init_balance +
            minting_cost.collateral + lovelace_amount
        ), f"Incorrect balance for token issuer address `{issuer_addr.address}`"

        token_utxo = cluster.get_utxo(address=issuer_addr.address,
                                      coins=[token])
        assert token_utxo and token_utxo[
            0].amount == token_amount, "The token was not minted"

        # check expected fees
        expected_fee_step1 = 168_977
        assert helpers.is_in_interval(tx_output_step1.fee,
                                      expected_fee_step1,
                                      frac=0.15)

        expected_fee_step2 = 371_111
        assert helpers.is_in_interval(tx_output_step2.fee,
                                      expected_fee_step2,
                                      frac=0.15)

        plutus_common.check_plutus_cost(
            plutus_cost=plutus_cost,
            expected_cost=[plutus_common.MINTING_COST],
        )

        # check tx_view
        tx_view.check_tx_view(cluster_obj=cluster,
                              tx_raw_output=tx_output_step2)

        dbsync_utils.check_tx(cluster_obj=cluster,
                              tx_raw_output=tx_output_step1)
        dbsync_utils.check_tx(cluster_obj=cluster,
                              tx_raw_output=tx_output_step2)
    def test_addr_registration_certificate_order(
        self,
        cluster: clusterlib.ClusterLib,
        pool_users: List[clusterlib.PoolUser],
        pool_users_disposable: List[clusterlib.PoolUser],
        use_build_cmd: bool,
    ):
        """Submit (de)registration certificates in single TX and check that the order matter.

        * create stake address registration cert
        * create stake address deregistration cert
        * register, deregister, register, deregister and register stake address in single TX
        * check that the address is registered
        * check that the balance for source address was correctly updated and that key deposit
          was needed
        * (optional) check records in db-sync
        """
        temp_template = f"{common.get_test_id(cluster)}_{use_build_cmd}"

        user_registered = pool_users_disposable[0]
        user_payment = pool_users[0].payment
        src_init_balance = cluster.get_address_balance(user_payment.address)

        # create stake address registration cert
        stake_addr_reg_cert_file = cluster.gen_stake_addr_registration_cert(
            addr_name=f"{temp_template}_addr0",
            stake_vkey_file=user_registered.stake.vkey_file)

        # create stake address deregistration cert
        stake_addr_dereg_cert_file = cluster.gen_stake_addr_deregistration_cert(
            addr_name=f"{temp_template}_addr0",
            stake_vkey_file=user_registered.stake.vkey_file)

        # register, deregister, register, deregister and register stake address in single TX
        # prove that the order matters
        tx_files = clusterlib.TxFiles(
            certificate_files=[
                stake_addr_reg_cert_file,
                stake_addr_dereg_cert_file,
                stake_addr_reg_cert_file,
                stake_addr_dereg_cert_file,
                stake_addr_reg_cert_file,
            ],
            signing_key_files=[
                user_payment.skey_file, user_registered.stake.skey_file
            ],
        )

        deposit = cluster.get_address_deposit()

        if use_build_cmd:
            tx_raw_output = cluster.build_tx(
                src_address=user_payment.address,
                tx_name=f"{temp_template}_reg_dereg_cert_order",
                tx_files=tx_files,
                fee_buffer=2_000_000,
                witness_override=len(tx_files.signing_key_files),
                deposit=deposit,
            )
            tx_signed = cluster.sign_tx(
                tx_body_file=tx_raw_output.out_file,
                signing_key_files=tx_files.signing_key_files,
                tx_name=f"{temp_template}_reg_dereg_cert_order",
            )
            cluster.submit_tx(tx_file=tx_signed, txins=tx_raw_output.txins)
        else:
            tx_raw_output = cluster.send_tx(
                src_address=user_payment.address,
                tx_name=f"{temp_template}_reg_dereg",
                tx_files=tx_files,
                deposit=deposit,
            )

        # check that the stake address is registered
        assert cluster.get_stake_addr_info(
            user_registered.stake.address).address

        # check that the balance for source address was correctly updated and that key deposit
        # was needed
        assert (
            cluster.get_address_balance(
                user_payment.address) == src_init_balance - tx_raw_output.fee -
            deposit
        ), f"Incorrect balance for source address `{user_payment.address}`"

        tx_db_record = dbsync_utils.check_tx(cluster_obj=cluster,
                                             tx_raw_output=tx_raw_output)
        if tx_db_record:
            assert user_registered.stake.address in tx_db_record.stake_registration
            assert user_registered.stake.address in tx_db_record.stake_deregistration
    def test_undelegate(
        self,
        cluster_manager: cluster_management.ClusterManager,
        cluster_and_pool: Tuple[clusterlib.ClusterLib, str],
    ):
        """Undelegate stake address.

        * submit registration certificate and delegate to pool
        * wait for first reward
        * undelegate stake address:

           - withdraw rewards to payment address
           - deregister stake address
           - re-register stake address

        * check that the key deposit was not returned
        * check that rewards were withdrawn
        * check that the stake address is still registered
        * check that the stake address is no longer delegated
        * (optional) check records in db-sync
        """
        cluster, pool_id = cluster_and_pool
        temp_template = common.get_test_id(cluster)

        clusterlib_utils.wait_for_epoch_interval(cluster_obj=cluster,
                                                 start=5,
                                                 stop=-20)
        init_epoch = cluster.get_epoch()

        # submit registration certificate and delegate to pool
        delegation_out = delegation.delegate_stake_addr(
            cluster_obj=cluster,
            addrs_data=cluster_manager.cache.addrs_data,
            temp_template=temp_template,
            pool_id=pool_id,
        )

        assert (
            cluster.get_epoch() == init_epoch
        ), "Delegation took longer than expected and would affect other checks"

        # check records in db-sync
        tx_db_deleg = dbsync_utils.check_tx(
            cluster_obj=cluster, tx_raw_output=delegation_out.tx_raw_output)
        delegation.db_check_delegation(
            pool_user=delegation_out.pool_user,
            db_record=tx_db_deleg,
            deleg_epoch=init_epoch,
            pool_id=delegation_out.pool_id,
        )

        src_address = delegation_out.pool_user.payment.address

        LOGGER.info("Waiting 4 epochs for first reward.")
        cluster.wait_for_new_epoch(new_epochs=4, padding_seconds=10)
        if not cluster.get_stake_addr_info(
                delegation_out.pool_user.stake.address).reward_account_balance:
            pytest.skip(
                f"User of pool '{pool_id}' hasn't received any rewards, cannot continue."
            )

        # make sure we have enough time to finish deregistration in one epoch
        clusterlib_utils.wait_for_epoch_interval(cluster_obj=cluster,
                                                 start=5,
                                                 stop=-40)

        # files for deregistering / re-registering stake address
        stake_addr_dereg_cert_file = cluster.gen_stake_addr_deregistration_cert(
            addr_name=f"{temp_template}_undeleg_addr0",
            stake_vkey_file=delegation_out.pool_user.stake.vkey_file,
        )
        stake_addr_reg_cert_file = cluster.gen_stake_addr_registration_cert(
            addr_name=f"{temp_template}_undeleg_addr0",
            stake_vkey_file=delegation_out.pool_user.stake.vkey_file,
        )
        tx_files_undeleg = clusterlib.TxFiles(
            certificate_files=[
                stake_addr_dereg_cert_file, stake_addr_reg_cert_file
            ],
            signing_key_files=[
                delegation_out.pool_user.payment.skey_file,
                delegation_out.pool_user.stake.skey_file,
            ],
        )

        src_payment_balance = cluster.get_address_balance(src_address)
        reward_balance = cluster.get_stake_addr_info(
            delegation_out.pool_user.stake.address).reward_account_balance

        # withdraw rewards to payment address; deregister and re-register stake address
        tx_raw_undeleg = cluster.send_tx(
            src_address=src_address,
            tx_name=f"{temp_template}_undeleg_withdraw",
            tx_files=tx_files_undeleg,
            withdrawals=[
                clusterlib.TxOut(
                    address=delegation_out.pool_user.stake.address, amount=-1)
            ],
        )

        # check that the key deposit was NOT returned and rewards were withdrawn
        assert (
            cluster.get_address_balance(src_address) == src_payment_balance -
            tx_raw_undeleg.fee + reward_balance
        ), f"Incorrect balance for source address `{src_address}`"

        # check that the stake address is no longer delegated
        stake_addr_info = cluster.get_stake_addr_info(
            delegation_out.pool_user.stake.address)
        assert stake_addr_info.address, f"Reward address is not registered: {stake_addr_info}"
        assert (not stake_addr_info.delegation
                ), f"Stake address is still delegated: {stake_addr_info}"

        this_epoch = cluster.wait_for_new_epoch(padding_seconds=20)
        assert cluster.get_stake_addr_info(
            delegation_out.pool_user.stake.address
        ).reward_account_balance, "No reward was received next epoch after undelegation"

        # check `transaction view` command
        tx_view.check_tx_view(cluster_obj=cluster,
                              tx_raw_output=tx_raw_undeleg)

        # check records in db-sync
        tx_db_undeleg = dbsync_utils.check_tx(cluster_obj=cluster,
                                              tx_raw_output=tx_raw_undeleg)
        if tx_db_undeleg:
            assert delegation_out.pool_user.stake.address in tx_db_undeleg.stake_deregistration
            assert delegation_out.pool_user.stake.address in tx_db_undeleg.stake_registration

            db_rewards = dbsync_utils.check_address_reward(
                address=delegation_out.pool_user.stake.address,
                epoch_from=init_epoch)
            assert db_rewards
            db_reward_epochs = sorted(r.spendable_epoch
                                      for r in db_rewards.rewards)
            assert db_reward_epochs[0] == init_epoch + 4
            assert this_epoch in db_reward_epochs
Exemple #8
0
    def test_delegate_deregister(
        self,
        cluster_lock_42stake: Tuple[clusterlib.ClusterLib, str],
        pool_user: delegation.PoolUserScript,
    ):
        """Delegate and deregister Plutus script stake address.

        * submit registration certificate and delegate stake address to pool
        * check that the stake address was delegated
        * withdraw rewards to payment address and deregister stake address
        * check that the key deposit was returned and rewards withdrawn
        * check that the stake address is no longer delegated
        * (optional) check records in db-sync
        """
        cluster, pool_id = cluster_lock_42stake
        temp_template = common.get_test_id(cluster)

        collateral_fund_deleg = 1_500_000_000
        collateral_fund_withdraw = 1_500_000_000
        collateral_fund_dereg = 1_500_000_000
        deleg_fund = 1_500_000_000
        dereg_fund = 1_500_000_000

        if cluster.get_stake_addr_info(pool_user.stake.address):
            pytest.skip(
                f"The Plutus script stake address '{pool_user.stake.address}' is already "
                "registered, cannot continue.")

        # Step 1: create Tx inputs for step 2 and step 3
        txouts_step1 = [
            # for collateral
            clusterlib.TxOut(address=pool_user.payment.address,
                             amount=collateral_fund_deleg),
            clusterlib.TxOut(address=pool_user.payment.address,
                             amount=collateral_fund_withdraw),
            clusterlib.TxOut(address=pool_user.payment.address,
                             amount=collateral_fund_dereg),
            clusterlib.TxOut(address=pool_user.payment.address,
                             amount=collateral_fund_dereg),
            # for delegation
            clusterlib.TxOut(address=pool_user.payment.address,
                             amount=deleg_fund),
            # for deregistration
            clusterlib.TxOut(address=pool_user.payment.address,
                             amount=dereg_fund),
        ]

        tx_files_step1 = clusterlib.TxFiles(
            signing_key_files=[pool_user.payment.skey_file], )
        tx_output_step1 = cluster.build_tx(
            src_address=pool_user.payment.address,
            tx_name=f"{temp_template}_step1",
            tx_files=tx_files_step1,
            txouts=txouts_step1,
            fee_buffer=2_000_000,
            # don't join 'change' and 'collateral' txouts, we need separate UTxOs
            join_txouts=False,
        )
        tx_signed_step1 = cluster.sign_tx(
            tx_body_file=tx_output_step1.out_file,
            signing_key_files=tx_files_step1.signing_key_files,
            tx_name=f"{temp_template}_step1",
        )
        cluster.submit_tx(tx_file=tx_signed_step1, txins=tx_output_step1.txins)

        txid_step1 = cluster.get_txid(tx_body_file=tx_output_step1.out_file)
        collateral_deleg = cluster.get_utxo(txin=f"{txid_step1}#1")
        collateral_withdraw = cluster.get_utxo(txin=f"{txid_step1}#2")
        collateral_dereg = cluster.get_utxo(txin=f"{txid_step1}#3")
        deleg_utxos = cluster.get_utxo(txin=f"{txid_step1}#4")
        dereg_utxos = cluster.get_utxo(txin=f"{txid_step1}#5")

        # Step 2: register and delegate

        clusterlib_utils.wait_for_epoch_interval(cluster_obj=cluster,
                                                 start=5,
                                                 stop=-20)
        init_epoch = cluster.get_epoch()

        # submit registration certificate and delegate to pool
        tx_raw_delegation_out, plutus_cost_deleg = delegate_stake_addr(
            cluster_obj=cluster,
            temp_template=temp_template,
            txins=deleg_utxos,
            collaterals=collateral_deleg,
            pool_user=pool_user,
            pool_id=pool_id,
            redeemer_file=plutus_common.REDEEMER_42,
        )

        assert (
            cluster.get_epoch() == init_epoch
        ), "Delegation took longer than expected and would affect other checks"

        tx_db_record = dbsync_utils.check_tx(
            cluster_obj=cluster, tx_raw_output=tx_raw_delegation_out)
        delegation.db_check_delegation(
            pool_user=pool_user,
            db_record=tx_db_record,
            deleg_epoch=init_epoch,
            pool_id=pool_id,
        )

        # Step 3: withdraw rewards and deregister

        reward_error = ""

        LOGGER.info("Waiting 4 epochs for first reward.")
        cluster.wait_for_new_epoch(new_epochs=4, padding_seconds=10)
        if not cluster.get_stake_addr_info(
                pool_user.stake.address).reward_account_balance:
            reward_error = f"User of pool '{pool_id}' hasn't received any rewards."

        # make sure we have enough time to finish deregistration in one epoch
        clusterlib_utils.wait_for_epoch_interval(cluster_obj=cluster,
                                                 start=5,
                                                 stop=-40)

        # submit deregistration certificate and withdraw rewards
        tx_raw_deregister_out = deregister_stake_addr(
            cluster_obj=cluster,
            temp_template=temp_template,
            txins=dereg_utxos,
            collaterals=[*collateral_withdraw, *collateral_dereg],
            pool_user=pool_user,
            redeemer_file=plutus_common.REDEEMER_42,
        )

        if reward_error:
            raise AssertionError(reward_error)

        # check tx_view of step 2 and step 3
        tx_view.check_tx_view(cluster_obj=cluster,
                              tx_raw_output=tx_raw_delegation_out)
        tx_view.check_tx_view(cluster_obj=cluster,
                              tx_raw_output=tx_raw_deregister_out)

        # compare cost of Plutus script with data from db-sync
        if tx_db_record:
            dbsync_utils.check_plutus_cost(
                redeemer_record=tx_db_record.redeemers[0],
                cost_record=plutus_cost_deleg[0])
Exemple #9
0
    def test_pay_stake_addr_from(
        self,
        cluster_manager: cluster_management.ClusterManager,
        cluster_pots: clusterlib.ClusterLib,
        registered_users: List[clusterlib.PoolUser],
        fund_src: str,
    ):
        """Send funds from the reserves or treasury pot to stake address.

        * generate an MIR certificate
        * submit a TX with the MIR certificate
        * check that the expected amount was added to the stake address reward account
        * (optional) check transaction in db-sync
        """
        temp_template = f"{common.get_test_id(cluster_pots)}_{fund_src}"
        cluster = cluster_pots
        amount = 50_000_000
        registered_user = registered_users[0]

        init_reward = cluster.get_stake_addr_info(
            registered_user.stake.address).reward_account_balance
        init_balance = cluster.get_address_balance(
            registered_user.payment.address)

        mir_cert = cluster.gen_mir_cert_stake_addr(
            stake_addr=registered_user.stake.address,
            reward=amount,
            tx_name=temp_template,
            use_treasury=fund_src == self.TREASURY,
        )
        tx_files = clusterlib.TxFiles(
            certificate_files=[mir_cert],
            signing_key_files=[
                registered_user.payment.skey_file,
                *cluster.genesis_keys.delegate_skeys,
            ],
        )

        # send the transaction at the beginning of an epoch
        if cluster.time_from_epoch_start() > (cluster.epoch_length_sec // 6):
            cluster.wait_for_new_epoch()

        LOGGER.info(
            f"Submitting MIR cert for transferring funds from {fund_src} to "
            f"'{registered_user.stake.address}' in epoch {cluster.get_epoch()} "
            f"on cluster instance {cluster_manager.cluster_instance_num}")
        tx_raw_output = cluster.send_tx(
            src_address=registered_user.payment.address,
            tx_name=temp_template,
            tx_files=tx_files,
        )

        assert (
            cluster.get_address_balance(
                registered_user.payment.address) == init_balance -
            tx_raw_output.fee
        ), f"Incorrect balance for source address `{registered_user.payment.address}`"

        cluster.wait_for_new_epoch()

        assert (
            cluster.get_stake_addr_info(
                registered_user.stake.address).reward_account_balance ==
            init_reward + amount
        ), f"Incorrect reward balance for stake address `{registered_user.stake.address}`"

        # check `transaction view` command
        tx_view.check_tx_view(cluster_obj=cluster, tx_raw_output=tx_raw_output)

        tx_db_record = dbsync_utils.check_tx(cluster_obj=cluster,
                                             tx_raw_output=tx_raw_output)
        if tx_db_record:
            stash_record = (tx_db_record.treasury[0] if fund_src
                            == self.TREASURY else tx_db_record.reserve[0])
            assert stash_record.amount == amount, (
                "Incorrect amount transferred using MIR certificate "
                f"({stash_record.amount} != {amount})")
            assert stash_record.address == registered_user.stake.address, (
                "Incorrect stake address "
                f"({stash_record.address} != {registered_user.stake.address})")
Exemple #10
0
    def test_transfer_to_treasury(
        self,
        cluster_manager: cluster_management.ClusterManager,
        cluster_pots: clusterlib.ClusterLib,
        pool_users: List[clusterlib.PoolUser],
    ):
        """Send funds from the reserves pot to the treasury pot.

        Expected to fail when Era < Alonzo.
        """
        temp_template = common.get_test_id(cluster_pots)
        cluster = cluster_pots
        pool_user = pool_users[0]
        amount = 10_000_000_000_000

        init_balance = cluster.get_address_balance(pool_user.payment.address)

        mir_cert = cluster.gen_mir_cert_to_treasury(transfer=amount,
                                                    tx_name=temp_template)
        tx_files = clusterlib.TxFiles(
            certificate_files=[mir_cert],
            signing_key_files=[
                pool_user.payment.skey_file,
                *cluster.genesis_keys.delegate_skeys
            ],
        )

        # send the transaction at the beginning of an epoch
        if cluster.time_from_epoch_start() > (cluster.epoch_length_sec // 6):
            cluster.wait_for_new_epoch()

        # fail is expected when Era < Alonzo
        if VERSIONS.cluster_era < VERSIONS.ALONZO:
            with pytest.raises(clusterlib.CLIError) as excinfo:
                cluster.send_tx(
                    src_address=pool_user.payment.address,
                    tx_name=temp_template,
                    tx_files=tx_files,
                )
            assert "MIRTransferNotCurrentlyAllowed" in str(excinfo.value)
            return

        LOGGER.info(
            f"Submitting MIR cert for transferring funds to treasury in epoch {cluster.get_epoch()}"
            f" on cluster instance {cluster_manager.cluster_instance_num}")
        tx_raw_output = cluster.send_tx(
            src_address=pool_user.payment.address,
            tx_name=temp_template,
            tx_files=tx_files,
        )

        assert (
            cluster.get_address_balance(
                pool_user.payment.address) == init_balance - tx_raw_output.fee
        ), f"Incorrect balance for source address `{pool_user.payment.address}`"

        # check `transaction view` command
        tx_view.check_tx_view(cluster_obj=cluster, tx_raw_output=tx_raw_output)

        tx_db_record = dbsync_utils.check_tx(cluster_obj=cluster,
                                             tx_raw_output=tx_raw_output)
        if tx_db_record:
            tx_epoch = cluster.get_epoch()

            assert tx_db_record.pot_transfers[0].reserves == -amount, (
                "Incorrect amount transferred from reserves "
                f"({tx_db_record.pot_transfers[0].reserves} != {-amount})")
            assert tx_db_record.pot_transfers[0].treasury == amount, (
                "Incorrect amount transferred to treasury "
                f"({tx_db_record.pot_transfers[0].treasury} != {amount})")

            cluster.wait_for_new_epoch()

            pots_records = _wait_for_ada_pots(epoch_from=tx_epoch)
            # normally `treasury[-1]` > `treasury[-2]`
            assert (pots_records[-1].treasury -
                    pots_records[-2].treasury) > amount
            # normally `reserves[-1]` < `reserves[-2]`
            assert (pots_records[-2].reserves -
                    pots_records[-1].reserves) > amount
    def test_witness_redeemer(
        self,
        cluster: clusterlib.ClusterLib,
        payment_addrs: List[clusterlib.AddressRecord],
        key: str,
    ):
        """Test minting a token with a Plutus script.

        Uses `cardano-cli transaction build` command for building the transactions.

        * fund the token issuer and create a UTxO for collateral
        * check that the expected amount was transferred to token issuer's address
        * mint the token using a Plutus script with required signer
        * check that the token was minted and collateral UTxO was not spent
        * check expected fees
        * check expected Plutus cost
        * (optional) check transactions in db-sync
        """
        # pylint: disable=too-many-locals
        temp_template = common.get_test_id(cluster)
        payment_addr = payment_addrs[0]
        issuer_addr = payment_addrs[1]

        lovelace_amount = 2_000_000
        token_amount = 5
        script_fund = 200_000_000

        minting_cost = plutus_common.compute_cost(
            execution_cost=plutus_common.MINTING_WITNESS_REDEEMER_COST,
            protocol_params=cluster.get_protocol_params(),
        )

        if key == "normal":
            redeemer_file = plutus_common.DATUM_WITNESS_GOLDEN_NORMAL
            signing_key_golden = plutus_common.SIGNING_KEY_GOLDEN
        else:
            redeemer_file = plutus_common.DATUM_WITNESS_GOLDEN_EXTENDED
            signing_key_golden = plutus_common.SIGNING_KEY_GOLDEN_EXTENDED

        issuer_init_balance = cluster.get_address_balance(issuer_addr.address)

        # Step 1: fund the token issuer

        mint_utxos, collateral_utxos, tx_output_step1 = _fund_issuer(
            cluster_obj=cluster,
            temp_template=temp_template,
            payment_addr=payment_addr,
            issuer_addr=issuer_addr,
            minting_cost=minting_cost,
            amount=script_fund,
        )

        # Step 2: mint the "qacoin"

        policyid = cluster.get_policyid(plutus_common.MINTING_PLUTUS_V1)
        asset_name = f"qacoin{clusterlib.get_rand_str(4)}".encode(
            "utf-8").hex()
        token = f"{policyid}.{asset_name}"
        mint_txouts = [
            clusterlib.TxOut(address=issuer_addr.address,
                             amount=token_amount,
                             coin=token)
        ]

        plutus_mint_data = [
            clusterlib.Mint(
                txouts=mint_txouts,
                script_file=plutus_common.MINTING_PLUTUS_V1,
                collaterals=collateral_utxos,
                redeemer_file=redeemer_file,
            )
        ]

        tx_files_step2 = clusterlib.TxFiles(
            signing_key_files=[issuer_addr.skey_file, signing_key_golden], )
        txouts_step2 = [
            clusterlib.TxOut(address=issuer_addr.address,
                             amount=lovelace_amount),
            *mint_txouts,
        ]
        tx_output_step2 = cluster.build_tx(
            src_address=payment_addr.address,
            tx_name=f"{temp_template}_step2",
            tx_files=tx_files_step2,
            txins=mint_utxos,
            txouts=txouts_step2,
            mint=plutus_mint_data,
            required_signers=[signing_key_golden],
        )
        plutus_cost = cluster.calculate_plutus_script_cost(
            src_address=payment_addr.address,
            tx_name=f"{temp_template}_step2",
            tx_files=tx_files_step2,
            txins=mint_utxos,
            txouts=txouts_step2,
            mint=plutus_mint_data,
            required_signers=[signing_key_golden],
        )
        # sign incrementally (just to check that it works)
        tx_signed_step2 = cluster.sign_tx(
            tx_body_file=tx_output_step2.out_file,
            signing_key_files=[issuer_addr.skey_file],
            tx_name=f"{temp_template}_step2_sign0",
        )
        tx_signed_step2_inc = cluster.sign_tx(
            tx_file=tx_signed_step2,
            signing_key_files=[signing_key_golden],
            tx_name=f"{temp_template}_step2_sign1",
        )
        cluster.submit_tx(tx_file=tx_signed_step2_inc, txins=mint_utxos)

        assert (
            cluster.get_address_balance(
                issuer_addr.address) == issuer_init_balance +
            minting_cost.collateral + lovelace_amount
        ), f"Incorrect balance for token issuer address `{issuer_addr.address}`"

        token_utxo = cluster.get_utxo(address=issuer_addr.address,
                                      coins=[token])
        assert token_utxo and token_utxo[
            0].amount == token_amount, "The token was not minted"

        # check expected fees
        expected_fee_step1 = 167_349
        assert helpers.is_in_interval(tx_output_step1.fee,
                                      expected_fee_step1,
                                      frac=0.15)

        expected_fee_step2 = 372_438
        assert helpers.is_in_interval(tx_output_step2.fee,
                                      expected_fee_step2,
                                      frac=0.15)

        plutus_common.check_plutus_cost(
            plutus_cost=plutus_cost,
            expected_cost=[plutus_common.MINTING_WITNESS_REDEEMER_COST],
        )

        dbsync_utils.check_tx(cluster_obj=cluster,
                              tx_raw_output=tx_output_step1)
        dbsync_utils.check_tx(cluster_obj=cluster,
                              tx_raw_output=tx_output_step2)
    def test_minting_context_equivalance(
            self, cluster: clusterlib.ClusterLib,
            payment_addrs: List[clusterlib.AddressRecord]):
        """Test context equivalence while minting a token.

        Uses `cardano-cli transaction build` command for building the transactions.

        * fund the token issuer and create a UTxO for collateral
        * check that the expected amount was transferred to token issuer's address
        * generate a dummy redeemer and a dummy Tx
        * derive the correct redeemer from the dummy Tx
        * mint the token using the derived redeemer
        * check that the token was minted and collateral UTxO was not spent
        * check expected Plutus cost
        * (optional) check transactions in db-sync
        """
        # pylint: disable=too-many-locals,too-many-statements
        temp_template = common.get_test_id(cluster)
        payment_addr = payment_addrs[0]
        issuer_addr = payment_addrs[1]

        lovelace_amount = 2_000_000
        token_amount = 5
        script_fund = 200_000_000

        minting_cost = plutus_common.compute_cost(
            execution_cost=plutus_common.MINTING_CONTEXT_EQUIVALENCE_COST,
            protocol_params=cluster.get_protocol_params(),
        )

        issuer_init_balance = cluster.get_address_balance(issuer_addr.address)

        # Step 1: fund the token issuer

        mint_utxos, collateral_utxos, tx_output_step1 = _fund_issuer(
            cluster_obj=cluster,
            temp_template=temp_template,
            payment_addr=payment_addr,
            issuer_addr=issuer_addr,
            minting_cost=minting_cost,
            amount=script_fund,
        )

        # Step 2: mint the "qacoin"

        invalid_hereafter = cluster.get_slot_no() + 1_000

        policyid = cluster.get_policyid(
            plutus_common.MINTING_CONTEXT_EQUIVALENCE_PLUTUS_V1)
        asset_name = f"qacoin{clusterlib.get_rand_str(4)}".encode(
            "utf-8").hex()
        token = f"{policyid}.{asset_name}"
        mint_txouts = [
            clusterlib.TxOut(address=issuer_addr.address,
                             amount=token_amount,
                             coin=token)
        ]

        tx_files_step2 = clusterlib.TxFiles(signing_key_files=[
            issuer_addr.skey_file, plutus_common.SIGNING_KEY_GOLDEN
        ], )
        txouts_step2 = [
            clusterlib.TxOut(address=issuer_addr.address,
                             amount=lovelace_amount),
            *mint_txouts,
        ]

        # generate a dummy redeemer in order to create a txbody from which
        # we can generate a tx and then derive the correct redeemer
        redeemer_file_dummy = Path(
            f"{temp_template}_dummy_script_context.redeemer")
        clusterlib_utils.create_script_context(
            cluster_obj=cluster, redeemer_file=redeemer_file_dummy)

        plutus_mint_data_dummy = [
            clusterlib.Mint(
                txouts=mint_txouts,
                script_file=plutus_common.
                MINTING_CONTEXT_EQUIVALENCE_PLUTUS_V1,
                collaterals=collateral_utxos,
                redeemer_file=redeemer_file_dummy,
            )
        ]

        tx_output_dummy = cluster.build_tx(
            src_address=payment_addr.address,
            tx_name=f"{temp_template}_dummy",
            tx_files=tx_files_step2,
            txins=mint_utxos,
            txouts=txouts_step2,
            mint=plutus_mint_data_dummy,
            required_signers=[plutus_common.SIGNING_KEY_GOLDEN],
            invalid_before=1,
            invalid_hereafter=invalid_hereafter,
            script_valid=False,
        )
        assert tx_output_dummy

        tx_file_dummy = cluster.sign_tx(
            tx_body_file=tx_output_dummy.out_file,
            signing_key_files=tx_files_step2.signing_key_files,
            tx_name=f"{temp_template}_dummy",
        )

        # generate the "real" redeemer
        redeemer_file = Path(f"{temp_template}_script_context.redeemer")

        try:
            clusterlib_utils.create_script_context(cluster_obj=cluster,
                                                   redeemer_file=redeemer_file,
                                                   tx_file=tx_file_dummy)
        except AssertionError as err:
            err_msg = str(err)
            if "DeserialiseFailure" in err_msg:
                pytest.xfail("DeserialiseFailure: see issue #944")
            if "TextEnvelopeTypeError" in err_msg and cluster.use_cddl:  # noqa: SIM106
                pytest.xfail(
                    "TextEnvelopeTypeError: `create-script-context` doesn't work with CDDL format"
                )
            else:
                raise

        plutus_mint_data = [
            plutus_mint_data_dummy[0]._replace(redeemer_file=redeemer_file)
        ]

        tx_output_step2 = cluster.build_tx(
            src_address=payment_addr.address,
            tx_name=f"{temp_template}_step2",
            tx_files=tx_files_step2,
            txins=mint_utxos,
            txouts=txouts_step2,
            mint=plutus_mint_data,
            required_signers=[plutus_common.SIGNING_KEY_GOLDEN],
            invalid_before=1,
            invalid_hereafter=invalid_hereafter,
        )

        # calculate cost of Plutus script
        plutus_cost_step2 = cluster.calculate_plutus_script_cost(
            src_address=payment_addr.address,
            tx_name=f"{temp_template}_step2",
            tx_files=tx_files_step2,
            txins=mint_utxos,
            txouts=txouts_step2,
            mint=plutus_mint_data,
            required_signers=[plutus_common.SIGNING_KEY_GOLDEN],
            invalid_before=1,
            invalid_hereafter=invalid_hereafter,
        )

        tx_signed_step2 = cluster.sign_tx(
            tx_body_file=tx_output_step2.out_file,
            signing_key_files=tx_files_step2.signing_key_files,
            tx_name=f"{temp_template}_step2",
        )
        cluster.submit_tx(tx_file=tx_signed_step2, txins=mint_utxos)

        assert (
            cluster.get_address_balance(
                issuer_addr.address) == issuer_init_balance +
            minting_cost.collateral + lovelace_amount
        ), f"Incorrect balance for token issuer address `{issuer_addr.address}`"

        token_utxo = cluster.get_utxo(address=issuer_addr.address,
                                      coins=[token])
        assert token_utxo and token_utxo[
            0].amount == token_amount, "The token was not minted"

        plutus_common.check_plutus_cost(
            plutus_cost=plutus_cost_step2,
            expected_cost=[plutus_common.MINTING_CONTEXT_EQUIVALENCE_COST],
        )

        # check tx_view
        tx_view.check_tx_view(cluster_obj=cluster,
                              tx_raw_output=tx_output_step2)

        dbsync_utils.check_tx(cluster_obj=cluster,
                              tx_raw_output=tx_output_step1)
        tx_db_record_step2 = dbsync_utils.check_tx(
            cluster_obj=cluster, tx_raw_output=tx_output_step2)
        # compare cost of Plutus script with data from db-sync
        if tx_db_record_step2:
            dbsync_utils.check_plutus_cost(
                redeemer_record=tx_db_record_step2.redeemers[0],
                cost_record=plutus_cost_step2[0])
    def test_two_scripts_minting(
            self, cluster: clusterlib.ClusterLib,
            payment_addrs: List[clusterlib.AddressRecord]):
        """Test minting two tokens with two different Plutus scripts.

        Uses `cardano-cli transaction build` command for building the transactions.

        * fund the token issuer and create a UTxO for collaterals
        * check that the expected amount was transferred to token issuer's address
        * mint the tokens using two different Plutus scripts
        * check that the tokens were minted and collateral UTxOs were not spent
        * check transaction view output
        * check expected fees
        * check expected Plutus cost
        * (optional) check transactions in db-sync
        """
        # pylint: disable=too-many-locals,too-many-statements
        temp_template = common.get_test_id(cluster)
        payment_addr = payment_addrs[0]
        issuer_addr = payment_addrs[1]

        lovelace_amount = 2_000_000
        token_amount = 5
        script_fund = 500_000_000

        # this is higher than `plutus_common.MINTING*_COST`, because the script context has changed
        # to include more stuff
        minting_cost_two = plutus_common.ExecutionCost(per_time=408_545_501,
                                                       per_space=1_126_016,
                                                       fixed_cost=94_428)
        minting_time_range_cost_two = plutus_common.ExecutionCost(
            per_time=427_707_230, per_space=1_188_952, fixed_cost=99_441)

        protocol_params = cluster.get_protocol_params()
        minting_cost1 = plutus_common.compute_cost(
            execution_cost=minting_cost_two, protocol_params=protocol_params)
        minting_cost2 = plutus_common.compute_cost(
            execution_cost=minting_time_range_cost_two,
            protocol_params=protocol_params)

        issuer_init_balance = cluster.get_address_balance(issuer_addr.address)

        # Step 1: fund the token issuer

        tx_files_step1 = clusterlib.TxFiles(
            signing_key_files=[payment_addr.skey_file], )
        txouts_step1 = [
            clusterlib.TxOut(address=issuer_addr.address, amount=script_fund),
            # for collaterals
            clusterlib.TxOut(address=issuer_addr.address,
                             amount=minting_cost1.collateral),
            clusterlib.TxOut(address=issuer_addr.address,
                             amount=minting_cost2.collateral),
        ]
        tx_output_step1 = cluster.build_tx(
            src_address=payment_addr.address,
            tx_name=f"{temp_template}_step1",
            tx_files=tx_files_step1,
            txouts=txouts_step1,
            fee_buffer=2_000_000,
            # don't join 'change' and 'collateral' txouts, we need separate UTxOs
            join_txouts=False,
        )
        tx_signed_step1 = cluster.sign_tx(
            tx_body_file=tx_output_step1.out_file,
            signing_key_files=tx_files_step1.signing_key_files,
            tx_name=f"{temp_template}_step1",
        )
        cluster.submit_tx(tx_file=tx_signed_step1, txins=tx_output_step1.txins)

        issuer_step1_balance = cluster.get_address_balance(issuer_addr.address)
        assert (
            issuer_step1_balance == issuer_init_balance + script_fund +
            minting_cost1.collateral + minting_cost2.collateral
        ), f"Incorrect balance for token issuer address `{issuer_addr.address}`"

        # Step 2: mint the "qacoins"

        txid_step1 = cluster.get_txid(tx_body_file=tx_output_step1.out_file)
        mint_utxos = cluster.get_utxo(txin=f"{txid_step1}#1")
        collateral_utxo1 = cluster.get_utxo(txin=f"{txid_step1}#2")
        collateral_utxo2 = cluster.get_utxo(txin=f"{txid_step1}#3")

        slot_step2 = cluster.get_slot_no()

        # "time range" qacoin
        slots_offset = 200
        timestamp_offset_ms = int(slots_offset * cluster.slot_length +
                                  5) * 1_000

        protocol_version = cluster.get_protocol_params(
        )["protocolVersion"]["major"]
        if protocol_version > 5:
            # POSIX timestamp + offset
            redeemer_value_timerange = (
                int(datetime.datetime.now().timestamp() * 1_000) +
                timestamp_offset_ms)
        else:
            # BUG: https://github.com/input-output-hk/cardano-node/issues/3090
            redeemer_value_timerange = 1_000_000_000_000

        policyid_timerange = cluster.get_policyid(
            plutus_common.MINTING_TIME_RANGE_PLUTUS_V1)
        asset_name_timerange = f"qacoint{clusterlib.get_rand_str(4)}".encode(
            "utf-8").hex()
        token_timerange = f"{policyid_timerange}.{asset_name_timerange}"
        mint_txouts_timerange = [
            clusterlib.TxOut(address=issuer_addr.address,
                             amount=token_amount,
                             coin=token_timerange)
        ]

        # "anyone can mint" qacoin
        redeemer_cbor_file = plutus_common.REDEEMER_42_CBOR
        policyid_anyone = cluster.get_policyid(plutus_common.MINTING_PLUTUS_V1)
        asset_name_anyone = f"qacoina{clusterlib.get_rand_str(4)}".encode(
            "utf-8").hex()
        token_anyone = f"{policyid_anyone}.{asset_name_anyone}"
        mint_txouts_anyone = [
            clusterlib.TxOut(address=issuer_addr.address,
                             amount=token_amount,
                             coin=token_anyone)
        ]

        # mint the tokens
        plutus_mint_data = [
            clusterlib.Mint(
                txouts=mint_txouts_timerange,
                script_file=plutus_common.MINTING_TIME_RANGE_PLUTUS_V1,
                collaterals=collateral_utxo1,
                redeemer_value=str(redeemer_value_timerange),
            ),
            clusterlib.Mint(
                txouts=mint_txouts_anyone,
                script_file=plutus_common.MINTING_PLUTUS_V1,
                collaterals=collateral_utxo2,
                redeemer_cbor_file=redeemer_cbor_file,
            ),
        ]

        tx_files_step2 = clusterlib.TxFiles(
            signing_key_files=[issuer_addr.skey_file], )
        txouts_step2 = [
            clusterlib.TxOut(address=issuer_addr.address,
                             amount=lovelace_amount),
            *mint_txouts_timerange,
            *mint_txouts_anyone,
        ]
        tx_output_step2 = cluster.build_tx(
            src_address=payment_addr.address,
            tx_name=f"{temp_template}_step2",
            tx_files=tx_files_step2,
            txins=mint_utxos,
            txouts=txouts_step2,
            mint=plutus_mint_data,
            invalid_before=slot_step2 - slots_offset,
            invalid_hereafter=slot_step2 + slots_offset,
        )
        plutus_cost = cluster.calculate_plutus_script_cost(
            src_address=payment_addr.address,
            tx_name=f"{temp_template}_step2",
            tx_files=tx_files_step2,
            txins=mint_utxos,
            txouts=txouts_step2,
            mint=plutus_mint_data,
            invalid_before=slot_step2 - slots_offset,
            invalid_hereafter=slot_step2 + slots_offset,
        )
        tx_signed_step2 = cluster.sign_tx(
            tx_body_file=tx_output_step2.out_file,
            signing_key_files=tx_files_step2.signing_key_files,
            tx_name=f"{temp_template}_step2",
        )
        cluster.submit_tx(tx_file=tx_signed_step2, txins=mint_utxos)

        assert (
            cluster.get_address_balance(
                issuer_addr.address) == issuer_init_balance +
            minting_cost1.collateral + minting_cost2.collateral +
            lovelace_amount
        ), f"Incorrect balance for token issuer address `{issuer_addr.address}`"

        token_utxo_timerange = cluster.get_utxo(address=issuer_addr.address,
                                                coins=[token_timerange])
        assert (token_utxo_timerange and token_utxo_timerange[0].amount
                == token_amount), "The 'timerange' token was not minted"

        token_utxo_anyone = cluster.get_utxo(address=issuer_addr.address,
                                             coins=[token_anyone])
        assert (token_utxo_anyone and token_utxo_anyone[0].amount
                == token_amount), "The 'anyone' token was not minted"

        # check expected fees
        expected_fee_step1 = 168_977
        assert helpers.is_in_interval(tx_output_step1.fee,
                                      expected_fee_step1,
                                      frac=0.15)

        expected_fee_step2 = 633_269
        assert helpers.is_in_interval(tx_output_step2.fee,
                                      expected_fee_step2,
                                      frac=0.15)

        plutus_common.check_plutus_cost(
            plutus_cost=plutus_cost,
            expected_cost=[minting_cost_two, minting_time_range_cost_two],
        )

        # check tx_view
        tx_view.check_tx_view(cluster_obj=cluster,
                              tx_raw_output=tx_output_step2)

        # check transactions in db-sync
        dbsync_utils.check_tx(cluster_obj=cluster,
                              tx_raw_output=tx_output_step1)
        dbsync_utils.check_tx(cluster_obj=cluster,
                              tx_raw_output=tx_output_step2)
    def test_time_range_minting(self, cluster: clusterlib.ClusterLib,
                                payment_addrs: List[clusterlib.AddressRecord]):
        """Test minting a token with a time constraints Plutus script.

        Uses `cardano-cli transaction build` command for building the transactions.

        * fund the token issuer and create a UTxO for collateral
        * check that the expected amount was transferred to token issuer's address
        * mint the token using a Plutus script
        * check that the token was minted and collateral UTxO was not spent
        * check expected fees
        * check expected Plutus cost
        * (optional) check transactions in db-sync
        """
        # pylint: disable=too-many-locals
        temp_template = common.get_test_id(cluster)
        payment_addr = payment_addrs[0]
        issuer_addr = payment_addrs[1]

        lovelace_amount = 2_000_000
        token_amount = 5
        script_fund = 200_000_000

        minting_cost = plutus_common.compute_cost(
            execution_cost=plutus_common.MINTING_TIME_RANGE_COST,
            protocol_params=cluster.get_protocol_params(),
        )

        issuer_init_balance = cluster.get_address_balance(issuer_addr.address)

        # Step 1: fund the token issuer

        mint_utxos, collateral_utxos, tx_output_step1 = _fund_issuer(
            cluster_obj=cluster,
            temp_template=temp_template,
            payment_addr=payment_addr,
            issuer_addr=issuer_addr,
            minting_cost=minting_cost,
            amount=script_fund,
        )

        # Step 2: mint the "qacoin"

        slot_step2 = cluster.get_slot_no()
        slots_offset = 200
        timestamp_offset_ms = int(slots_offset * cluster.slot_length +
                                  5) * 1_000

        protocol_version = cluster.get_protocol_params(
        )["protocolVersion"]["major"]
        if protocol_version > 5:
            # POSIX timestamp + offset
            redeemer_value = int(datetime.datetime.now().timestamp() *
                                 1_000) + timestamp_offset_ms
        else:
            # BUG: https://github.com/input-output-hk/cardano-node/issues/3090
            redeemer_value = 1_000_000_000_000

        policyid = cluster.get_policyid(
            plutus_common.MINTING_TIME_RANGE_PLUTUS_V1)
        asset_name = f"qacoin{clusterlib.get_rand_str(4)}".encode(
            "utf-8").hex()
        token = f"{policyid}.{asset_name}"
        mint_txouts = [
            clusterlib.TxOut(address=issuer_addr.address,
                             amount=token_amount,
                             coin=token)
        ]

        plutus_mint_data = [
            clusterlib.Mint(
                txouts=mint_txouts,
                script_file=plutus_common.MINTING_TIME_RANGE_PLUTUS_V1,
                collaterals=collateral_utxos,
                redeemer_value=str(redeemer_value),
            )
        ]

        tx_files_step2 = clusterlib.TxFiles(
            signing_key_files=[issuer_addr.skey_file], )
        txouts_step2 = [
            clusterlib.TxOut(address=issuer_addr.address,
                             amount=lovelace_amount),
            *mint_txouts,
        ]
        tx_output_step2 = cluster.build_tx(
            src_address=payment_addr.address,
            tx_name=f"{temp_template}_step2",
            tx_files=tx_files_step2,
            txins=mint_utxos,
            txouts=txouts_step2,
            mint=plutus_mint_data,
            invalid_before=slot_step2 - slots_offset,
            invalid_hereafter=slot_step2 + slots_offset,
        )
        plutus_cost = cluster.calculate_plutus_script_cost(
            src_address=payment_addr.address,
            tx_name=f"{temp_template}_step2",
            tx_files=tx_files_step2,
            txins=mint_utxos,
            txouts=txouts_step2,
            mint=plutus_mint_data,
            invalid_before=slot_step2 - slots_offset,
            invalid_hereafter=slot_step2 + slots_offset,
        )
        tx_signed_step2 = cluster.sign_tx(
            tx_body_file=tx_output_step2.out_file,
            signing_key_files=tx_files_step2.signing_key_files,
            tx_name=f"{temp_template}_step2",
        )
        cluster.submit_tx(tx_file=tx_signed_step2, txins=mint_utxos)

        assert (
            cluster.get_address_balance(
                issuer_addr.address) == issuer_init_balance +
            minting_cost.collateral + lovelace_amount
        ), f"Incorrect balance for token issuer address `{issuer_addr.address}`"

        token_utxo = cluster.get_utxo(address=issuer_addr.address,
                                      coins=[token])
        assert token_utxo and token_utxo[
            0].amount == token_amount, "The token was not minted"

        # check expected fees
        expected_fee_step1 = 167_349
        assert helpers.is_in_interval(tx_output_step1.fee,
                                      expected_fee_step1,
                                      frac=0.15)

        expected_fee_step2 = 411_175
        assert helpers.is_in_interval(tx_output_step2.fee,
                                      expected_fee_step2,
                                      frac=0.15)

        plutus_common.check_plutus_cost(
            plutus_cost=plutus_cost,
            expected_cost=[plutus_common.MINTING_TIME_RANGE_COST],
        )

        # check tx_view
        tx_view.check_tx_view(cluster_obj=cluster,
                              tx_raw_output=tx_output_step2)

        dbsync_utils.check_tx(cluster_obj=cluster,
                              tx_raw_output=tx_output_step1)
        dbsync_utils.check_tx(cluster_obj=cluster,
                              tx_raw_output=tx_output_step2)
Exemple #15
0
    def test_pay_unregistered_stake_addr_from(
        self,
        cluster_pots: clusterlib.ClusterLib,
        pool_users: List[clusterlib.PoolUser],
        fund_src: str,
    ):
        """Send funds from the reserves or treasury pot to unregistered stake address.

        * generate an MIR certificate
        * submit a TX with the MIR certificate
        * check that the amount was NOT added to the stake address reward account
        """
        temp_template = helpers.get_func_name()
        cluster = cluster_pots
        pool_user = pool_users[0]

        if fund_src == self.TREASURY:
            amount = 1_500_000_000_000
        else:
            amount = 50_000_000_000_000

        init_balance = cluster.get_address_balance(pool_user.payment.address)

        mir_cert = cluster.gen_mir_cert_stake_addr(
            stake_addr=pool_user.stake.address,
            reward=amount,
            tx_name=temp_template,
            use_treasury=fund_src == self.TREASURY,
        )
        tx_files = clusterlib.TxFiles(
            certificate_files=[mir_cert],
            signing_key_files=[
                pool_user.payment.skey_file,
                *cluster.genesis_keys.delegate_skeys,
            ],
        )

        # send the transaction at the beginning of an epoch
        if cluster.time_from_epoch_start() > (cluster.epoch_length_sec // 6):
            cluster.wait_for_new_epoch()

        tx_raw_output = cluster.send_tx(
            src_address=pool_user.payment.address,
            tx_name=temp_template,
            tx_files=tx_files,
        )

        tx_epoch = cluster.get_epoch()

        assert (
            cluster.get_address_balance(pool_user.payment.address)
            == init_balance - tx_raw_output.fee
        ), f"Incorrect balance for source address `{pool_user.payment.address}`"

        tx_db_record = dbsync_utils.check_tx(cluster_obj=cluster, tx_raw_output=tx_raw_output)
        if tx_db_record:
            if fund_src == self.TREASURY:
                assert tx_db_record.treasury[0].amount == amount, (
                    "Incorrect amount transferred from treasury "
                    f"({tx_db_record.treasury[0].amount} != {amount})"
                )
            else:
                assert tx_db_record.reserve[0].amount == amount, (
                    "Incorrect amount transferred from reserve "
                    f"({tx_db_record.reserve[0].amount} != {amount})"
                )

        cluster.wait_for_new_epoch()

        assert not cluster.get_stake_addr_info(
            pool_user.stake.address
        ).reward_account_balance, (
            f"Reward was added for unregistered stake address `{pool_user.stake.address}`"
        )

        if tx_db_record:
            # check that the amount was not transferred out of the pot
            pots_records = list(dbsync_utils.query_ada_pots(epoch_from=tx_epoch))
            if fund_src == self.TREASURY:
                # normally `treasury[-1]` > `treasury[-2]`
                assert abs(pots_records[-1].treasury - pots_records[-2].treasury) < amount
            else:
                # normally `reserves[-1]` < `reserves[-2]`
                assert abs(pots_records[-2].reserves - pots_records[-1].reserves) < amount
Exemple #16
0
def deregister_stake_addr(
    cluster_obj: clusterlib.ClusterLib,
    temp_template: str,
    txins: List[clusterlib.UTXOData],
    collaterals: List[clusterlib.UTXOData],
    pool_user: delegation.PoolUserScript,
    redeemer_file: Path,
) -> clusterlib.TxRawOutput:
    """Deregister stake address."""
    src_payment_balance = cluster_obj.get_address_balance(
        pool_user.payment.address)
    reward_balance = cluster_obj.get_stake_addr_info(
        pool_user.stake.address).reward_account_balance

    # create stake address deregistration cert
    stake_addr_dereg_cert = cluster_obj.gen_stake_addr_deregistration_cert(
        addr_name=f"{temp_template}_addr0",
        stake_script_file=pool_user.stake.script_file,
    )

    # withdraw rewards to payment address, deregister stake address
    withdrawal_script = clusterlib.ScriptWithdrawal(
        txout=clusterlib.TxOut(address=pool_user.stake.address, amount=-1),
        script_file=pool_user.stake.script_file,
        collaterals=[collaterals[0]],
        redeemer_file=redeemer_file,
    )
    dereg_cert_script = clusterlib.ComplexCert(
        certificate_file=stake_addr_dereg_cert,
        script_file=pool_user.stake.script_file,
        collaterals=[collaterals[1]],
        redeemer_file=redeemer_file,
    )

    tx_files = clusterlib.TxFiles(
        signing_key_files=[pool_user.payment.skey_file])

    tx_raw_output = cluster_obj.build_tx(
        src_address=pool_user.payment.address,
        tx_name=f"{temp_template}_dereg_withdraw",
        txins=txins,
        tx_files=tx_files,
        complex_certs=[dereg_cert_script],
        fee_buffer=2_000_000,
        script_withdrawals=[withdrawal_script],
        witness_override=len(tx_files.signing_key_files),
    )
    # calculate cost of Plutus script
    plutus_cost = cluster_obj.calculate_plutus_script_cost(
        src_address=pool_user.payment.address,
        tx_name=f"{temp_template}_dereg_withdraw",
        txins=txins,
        tx_files=tx_files,
        complex_certs=[dereg_cert_script],
        fee_buffer=2_000_000,
        script_withdrawals=[withdrawal_script],
        witness_override=len(tx_files.signing_key_files),
    )
    tx_signed = cluster_obj.sign_tx(
        tx_body_file=tx_raw_output.out_file,
        signing_key_files=tx_files.signing_key_files,
        tx_name=f"{temp_template}_reg_deleg",
    )

    cluster_obj.submit_tx(tx_file=tx_signed, txins=tx_raw_output.txins)

    # check that the key deposit was returned and rewards withdrawn
    assert (
        cluster_obj.get_address_balance(
            pool_user.payment.address) == src_payment_balance -
        tx_raw_output.fee + reward_balance + cluster_obj.get_address_deposit()
    ), f"Incorrect balance for source address `{pool_user.payment.address}`"

    # check that the stake address is no longer delegated
    stake_addr_info = cluster_obj.get_stake_addr_info(pool_user.stake.address)
    assert not stake_addr_info.delegation, f"Stake address is still delegated: {stake_addr_info}"

    tx_db_dereg = dbsync_utils.check_tx(cluster_obj=cluster_obj,
                                        tx_raw_output=tx_raw_output)
    if tx_db_dereg:
        assert pool_user.stake.address in tx_db_dereg.stake_deregistration

        # compare cost of Plutus script with data from db-sync
        dbsync_utils.check_plutus_cost(
            redeemer_record=tx_db_dereg.redeemers[0],
            cost_record=plutus_cost[0])

    return tx_raw_output
Exemple #17
0
    def test_pay_stake_addr_from_both(
        self,
        cluster_manager: cluster_management.ClusterManager,
        cluster_pots: clusterlib.ClusterLib,
        registered_users: List[clusterlib.PoolUser],
    ):
        """Send funds from the reserves and treasury pots to stake address.

        * generate an MIR certificate for transferring from treasury
        * generate an MIR certificate for transferring from reserves
        * submit a TX with the treasury MIR certificate
        * in the same epoch as the previous TX, submit a TX with the reserves MIR certificate
        * check that the expected amount was added to the stake address reward account
        * (optional) check transactions in db-sync
        """
        cluster = cluster_pots
        temp_template = common.get_test_id(cluster)
        amount = 50_000_000
        registered_user = registered_users[0]

        init_reward = cluster.get_stake_addr_info(
            registered_user.stake.address).reward_account_balance
        init_balance = cluster.get_address_balance(
            registered_user.payment.address)

        mir_cert_treasury = cluster.gen_mir_cert_stake_addr(
            stake_addr=registered_user.stake.address,
            reward=amount,
            tx_name=f"{temp_template}_treasury",
            use_treasury=True,
        )
        tx_files_treasury = clusterlib.TxFiles(
            certificate_files=[mir_cert_treasury],
            signing_key_files=[
                registered_user.payment.skey_file,
                *cluster.genesis_keys.delegate_skeys,
            ],
        )

        mir_cert_reserves = cluster.gen_mir_cert_stake_addr(
            stake_addr=registered_user.stake.address,
            reward=amount,
            tx_name=f"{temp_template}_reserves",
        )
        tx_files_reserves = clusterlib.TxFiles(
            certificate_files=[mir_cert_reserves],
            signing_key_files=[
                registered_user.payment.skey_file,
                *cluster.genesis_keys.delegate_skeys,
            ],
        )

        # send the transaction at the beginning of an epoch
        if cluster.time_from_epoch_start() > (cluster.epoch_length_sec // 6):
            cluster.wait_for_new_epoch()

        LOGGER.info(
            f"Submitting MIR cert for transferring funds from treasury to "
            f"'{registered_user.stake.address}' in epoch {cluster.get_epoch()} "
            f"on cluster instance {cluster_manager.cluster_instance_num}")
        tx_raw_output_treasury = cluster.send_tx(
            src_address=registered_user.payment.address,
            tx_name=f"{temp_template}_treasury",
            tx_files=tx_files_treasury,
        )

        time.sleep(2)

        LOGGER.info(
            f"Submitting MIR cert for transferring funds from reserves to "
            f"'{registered_user.stake.address}' in epoch {cluster.get_epoch()} "
            f"on cluster instance {cluster_manager.cluster_instance_num}")
        tx_raw_output_reserves = cluster.send_tx(
            src_address=registered_user.payment.address,
            tx_name=f"{temp_template}_reserves",
            tx_files=tx_files_reserves,
        )

        assert (
            cluster.get_address_balance(
                registered_user.payment.address) == init_balance -
            tx_raw_output_treasury.fee - tx_raw_output_reserves.fee
        ), f"Incorrect balance for source address `{registered_user.payment.address}`"

        cluster.wait_for_new_epoch()

        assert (
            cluster.get_stake_addr_info(
                registered_user.stake.address).reward_account_balance ==
            init_reward + amount * 2
        ), f"Incorrect reward balance for stake address `{registered_user.stake.address}`"

        tx_db_record_treasury = dbsync_utils.check_tx(
            cluster_obj=cluster, tx_raw_output=tx_raw_output_treasury)
        if tx_db_record_treasury:
            tx_db_record_reserves = dbsync_utils.check_tx(
                cluster_obj=cluster, tx_raw_output=tx_raw_output_reserves)
            assert tx_db_record_reserves

            assert (
                not tx_db_record_treasury.reserve
            ), f"Reserve record is not empty: {tx_db_record_treasury.reserve}"
            assert (
                not tx_db_record_reserves.treasury
            ), f"Treasury record is not empty: {tx_db_record_reserves.treasury}"

            db_treasury = tx_db_record_treasury.treasury[0]
            assert db_treasury.amount == amount, (
                "Incorrect amount transferred using MIR certificate "
                f"({db_treasury.amount} != {amount})")
            assert db_treasury.address == registered_user.stake.address, (
                "Incorrect stake address "
                f"({db_treasury.address} != {registered_user.stake.address})")

            db_reserve = tx_db_record_reserves.reserve[0]
            assert db_reserve.amount == amount, (
                "Incorrect amount transferred using MIR certificate "
                f"({db_reserve.amount} != {amount})")
            assert db_reserve.address == registered_user.stake.address, (
                "Incorrect stake address "
                f"({db_reserve.address} != {registered_user.stake.address})")
Exemple #18
0
    def test_lobster_name(self, cluster: clusterlib.ClusterLib,
                          payment_addrs: List[clusterlib.AddressRecord]):
        """Test the Lobster Challenge.

        Uses `cardano-cli transaction build` command for building the transactions.

        * fund token issuer and create a UTxO for collateral
        * mint the LobsterNFT token
        * deploy the LobsterNFT token to address of lobster spending script
        * generate random votes and determine the expected final value
        * perform voting and check that the final value matches the expected value
        * (optional) check transactions in db-sync
        """
        # pylint: disable=too-many-locals,too-many-statements
        temp_template = common.get_test_id(cluster)
        payment_addr = payment_addrs[0]
        issuer_addr = payment_addrs[1]

        votes_num = 50
        names_num = 1219
        io_random_seed = 42

        issuer_fund = 200_000_000
        lovelace_setup_amount = 1_724_100
        lovelace_vote_amount = 2_034_438
        collateral_amount = 20_000_000
        nft_amount = 1

        # Step 1: fund the token issuer and create UTXO for collaterals

        mint_utxos, collateral_utxos, tx_output_step1 = _fund_issuer(
            cluster_obj=cluster,
            temp_template=temp_template,
            payment_addr=payment_addr,
            issuer_addr=issuer_addr,
            amount=issuer_fund,
            collateral_amount=collateral_amount,
        )

        # Step 2: mint the LobsterNFT token

        lobster_nft_token, token_utxos_step2, tx_output_step2 = _mint_lobster_nft(
            cluster_obj=cluster,
            temp_template=temp_template,
            issuer_addr=issuer_addr,
            mint_utxos=mint_utxos,
            collateral_utxos=collateral_utxos,
            nft_amount=nft_amount,
            lovelace_amount=lovelace_setup_amount,
        )

        # Step 3: deploy the LobsterNFT token to script address

        script_address, token_utxos_step3, tx_output_step3 = _deploy_lobster_nft(
            cluster_obj=cluster,
            temp_template=temp_template,
            issuer_addr=issuer_addr,
            token_utxos=token_utxos_step2,
            lobster_nft_token=lobster_nft_token,
            nft_amount=nft_amount,
            lovelace_amount=lovelace_setup_amount,
        )

        tx_outputs_all = [tx_output_step1, tx_output_step2, tx_output_step3]

        # Step 4: prepare for voting

        # there's 50 votes, each vote is int between 1 and 100
        votes = [random.randint(1, 100) for __ in range(votes_num)]
        _votes_sum = sum(votes)
        # Add "random" seed to the sum of all votes. Taking the remainder after
        # division by the number of potential names (`names_num`) gives us the
        # final counter value.
        # The final counter value is used as an index. Looking into the list of
        # names, we can see the name the index points to. We don't need to do
        # that in automated test, we will just check that the final counter
        # value matches the expected counter value.
        expected_counter_val = (io_random_seed + _votes_sum) % names_num
        votes.append(expected_counter_val)

        # Step 5: vote

        other_policyid = cluster.get_policyid(OTHER_MINT_PLUTUS)
        asset_name_counter = b"LobsterCounter".hex()
        asset_name_votes = b"LobsterVotes".hex()
        counter_token = f"{other_policyid}.{asset_name_counter}"
        votes_token = f"{other_policyid}.{asset_name_votes}"

        vote_utxos = token_utxos_step3
        vote_counter = 0
        utxo_votes_token: Optional[clusterlib.UTXOData] = None
        utxo_counter_token: Optional[clusterlib.UTXOData] = None
        for vote_num, vote_val in enumerate(votes, start=1):
            # normal votes
            if vote_num <= votes_num:
                vote_counter += vote_val
                mint_val = vote_val
            # final IO vote
            else:
                # set new counter value to `(seed + counter value) % number of names`
                # and burn excesive LobsterCounter tokens
                mint_val = vote_val - vote_counter
                vote_counter = vote_val

            txouts = [
                # Lovelace amount
                clusterlib.TxOut(
                    address=script_address,
                    amount=lovelace_vote_amount,
                    datum_hash=LOBSTER_DATUM_HASH,
                ),
                # LobsterNFT token
                clusterlib.TxOut(
                    address=script_address,
                    amount=nft_amount,
                    coin=lobster_nft_token,
                    datum_hash=LOBSTER_DATUM_HASH,
                ),
                # LobsterCounter token
                clusterlib.TxOut(
                    address=script_address,
                    amount=vote_counter,
                    coin=counter_token,
                    datum_hash=LOBSTER_DATUM_HASH,
                ),
                # LobsterVotes token
                clusterlib.TxOut(
                    address=script_address,
                    amount=vote_num,
                    coin=votes_token,
                    datum_hash=LOBSTER_DATUM_HASH,
                ),
            ]

            mint_txouts = [
                # mint new LobsterCounter tokens
                clusterlib.TxOut(
                    address=script_address,
                    amount=mint_val,
                    coin=counter_token,
                    datum_hash=LOBSTER_DATUM_HASH,
                ),
                # mint 1 new LobsterVotes token
                clusterlib.TxOut(
                    address=script_address,
                    amount=1,
                    coin=votes_token,
                    datum_hash=LOBSTER_DATUM_HASH,
                ),
            ]
            mint_script_data = [
                clusterlib.Mint(
                    txouts=mint_txouts,
                    script_file=OTHER_MINT_PLUTUS,
                    redeemer_value="[]",
                )
            ]

            txin_script_data = [
                clusterlib.ScriptTxIn(
                    txins=vote_utxos,
                    script_file=LOBSTER_PLUTUS,
                    collaterals=collateral_utxos,
                    datum_value="[]",
                    redeemer_value="[]",
                )
            ]

            tx_files = clusterlib.TxFiles(signing_key_files=[
                payment_addr.skey_file, issuer_addr.skey_file
            ], )
            funds_txin = cluster.get_utxo_with_highest_amount(
                address=payment_addr.address)
            tx_output_vote = cluster.build_tx(
                src_address=payment_addr.address,
                tx_name=f"{temp_template}_voting_{vote_num}",
                txins=[funds_txin],
                tx_files=tx_files,
                txouts=txouts,
                script_txins=txin_script_data,
                mint=mint_script_data,
            )
            tx_signed = cluster.sign_tx(
                tx_body_file=tx_output_vote.out_file,
                signing_key_files=tx_files.signing_key_files,
                tx_name=f"{temp_template}_voting_{vote_num}",
            )
            cluster.submit_tx(tx_file=tx_signed, txins=vote_utxos)

            tx_outputs_all.append(tx_output_vote)

            txid_vote = cluster.get_txid(tx_body_file=tx_output_vote.out_file)
            vote_utxos = cluster.get_utxo(txin=f"{txid_vote}#1")

            # check expected balances
            utxos_lovelace = [
                u for u in vote_utxos if u.coin == clusterlib.DEFAULT_COIN
            ][0]
            assert (
                utxos_lovelace.amount == lovelace_vote_amount
            ), f"Incorrect Lovelace balance for script address `{script_address}`"

            utxo_votes_token = [
                u for u in vote_utxos if u.coin == votes_token
            ][0]
            assert (
                utxo_votes_token.amount == vote_num
            ), f"Incorrect LobsterVotes token balance for script address `{script_address}`"

            utxo_counter_token = [
                u for u in vote_utxos if u.coin == counter_token
            ][0]
            assert (
                utxo_counter_token.amount == vote_counter
            ), f"Incorrect LobsterCounter token balance for script address `{script_address}`"

        assert (
            utxo_counter_token
            and utxo_counter_token.amount == expected_counter_val
        ), "Final balance of LobsterCounter token doesn't match the expected balance"

        # check transactions in db-sync
        for tx_out_rec in tx_outputs_all:
            dbsync_utils.check_tx(cluster_obj=cluster,
                                  tx_raw_output=tx_out_rec)
Exemple #19
0
    def test_pay_multi_stake_addrs(
        self,
        cluster_manager: cluster_management.ClusterManager,
        cluster_pots: clusterlib.ClusterLib,
        registered_users: List[clusterlib.PoolUser],
    ):
        """Send funds from the reserves and treasury pots to multiple stake addresses in single TX.

        * generate an MIR certificates for transferring from treasury for each stake address
        * generate an MIR certificates for transferring from reserves for each stake address
        * submit a TX with all the MIR certificates generated in previous steps
        * check that the expected amount was added to all stake address reward accounts
        * (optional) check transaction in db-sync
        """
        cluster = cluster_pots
        temp_template = common.get_test_id(cluster)
        amount_treasury = 50_000_000
        amount_reserves = 60_000_000

        init_reward_u0 = cluster.get_stake_addr_info(
            registered_users[0].stake.address).reward_account_balance
        init_reward_u1 = cluster.get_stake_addr_info(
            registered_users[1].stake.address).reward_account_balance
        init_balance = cluster.get_address_balance(
            registered_users[0].payment.address)

        mir_cert_treasury_u0 = cluster.gen_mir_cert_stake_addr(
            stake_addr=registered_users[0].stake.address,
            reward=amount_treasury,
            tx_name=f"{temp_template}_treasury_u0",
            use_treasury=True,
        )
        mir_cert_reserves_u0 = cluster.gen_mir_cert_stake_addr(
            stake_addr=registered_users[0].stake.address,
            reward=amount_reserves,
            tx_name=f"{temp_template}_reserves_u0",
        )
        mir_cert_treasury_u1 = cluster.gen_mir_cert_stake_addr(
            stake_addr=registered_users[1].stake.address,
            reward=amount_treasury,
            tx_name=f"{temp_template}_treasury_u1",
            use_treasury=True,
        )
        mir_cert_reserves_u1 = cluster.gen_mir_cert_stake_addr(
            stake_addr=registered_users[1].stake.address,
            reward=amount_reserves,
            tx_name=f"{temp_template}_reserves_u1",
        )

        tx_files = clusterlib.TxFiles(
            certificate_files=[
                mir_cert_treasury_u0,
                mir_cert_reserves_u0,
                mir_cert_treasury_u1,
                mir_cert_reserves_u1,
            ],
            signing_key_files=[
                registered_users[0].payment.skey_file,
                *cluster.genesis_keys.delegate_skeys,
            ],
        )

        # send the transaction at the beginning of an epoch
        if cluster.time_from_epoch_start() > (cluster.epoch_length_sec // 6):
            cluster.wait_for_new_epoch()

        LOGGER.info(
            f"Submitting MIR cert for transferring funds from treasury and reserves to "
            f"multiple stake addresses in epoch {cluster.get_epoch()} "
            f"on cluster instance {cluster_manager.cluster_instance_num}")
        tx_raw_output = cluster.send_tx(
            src_address=registered_users[0].payment.address,
            tx_name=temp_template,
            tx_files=tx_files,
        )

        assert (
            cluster.get_address_balance(
                registered_users[0].payment.address) == init_balance -
            tx_raw_output.fee
        ), f"Incorrect balance for source address `{registered_users[0].payment.address}`"

        cluster.wait_for_new_epoch()

        assert (
            cluster.get_stake_addr_info(
                registered_users[0].stake.address).reward_account_balance ==
            init_reward_u0 + amount_treasury + amount_reserves
        ), f"Incorrect reward balance for stake address `{registered_users[0].stake.address}`"
        assert (
            cluster.get_stake_addr_info(
                registered_users[1].stake.address).reward_account_balance ==
            init_reward_u1 + amount_treasury + amount_reserves
        ), f"Incorrect reward balance for stake address `{registered_users[1].stake.address}`"

        tx_db_record = dbsync_utils.check_tx(cluster_obj=cluster,
                                             tx_raw_output=tx_raw_output)
        if tx_db_record:
            db_treasury_u0 = tx_db_record.treasury[0]
            db_reserve_u0 = tx_db_record.reserve[0]
            assert db_treasury_u0.amount == amount_treasury, (
                "Incorrect amount transferred using MIR certificate "
                f"({db_treasury_u0.amount} != {amount_treasury})")
            assert db_treasury_u0.address == registered_users[0].stake.address, (
                "Incorrect stake address "
                f"({db_treasury_u0.address} != {registered_users[0].stake.address})"
            )
            assert db_reserve_u0.amount == amount_reserves, (
                "Incorrect amount transferred using MIR certificate "
                f"({db_reserve_u0.amount} != {amount_reserves})")
            assert db_reserve_u0.address == registered_users[0].stake.address, (
                "Incorrect stake address "
                f"({db_reserve_u0.address} != {registered_users[0].stake.address})"
            )

            db_treasury_u1 = tx_db_record.treasury[1]
            db_reserve_u1 = tx_db_record.reserve[1]
            assert db_treasury_u1.amount == amount_treasury, (
                "Incorrect amount transferred using MIR certificate "
                f"({db_treasury_u1.amount} != {amount_treasury})")
            assert db_treasury_u1.address == registered_users[1].stake.address, (
                "Incorrect stake address "
                f"({db_treasury_u1.address} != {registered_users[1].stake.address})"
            )
            assert db_reserve_u1.amount == amount_reserves, (
                "Incorrect amount transferred using MIR certificate "
                f"({db_reserve_u1.amount} != {amount_reserves})")
            assert db_reserve_u1.address == registered_users[1].stake.address, (
                "Incorrect stake address "
                f"({db_reserve_u1.address} != {registered_users[1].stake.address})"
            )
    def test_deregister(
        self,
        cluster_manager: cluster_management.ClusterManager,
        cluster_and_pool: Tuple[clusterlib.ClusterLib, str],
    ):
        """Deregister stake address.

        * create two payment addresses that share single stake address
        * register and delegate the stake address to pool
        * attempt to deregister the stake address - deregistration is expected to fail
          because there are rewards in the stake address
        * withdraw rewards to payment address and deregister stake address
        * check that the key deposit was returned and rewards withdrawn
        * check that the stake address is no longer delegated
        * (optional) check records in db-sync
        """
        cluster, pool_id = cluster_and_pool
        temp_template = common.get_test_id(cluster)

        # create two payment addresses that share single stake address (just to test that
        # delegation works as expected even under such circumstances)
        stake_addr_rec = clusterlib_utils.create_stake_addr_records(
            f"{temp_template}_addr0", cluster_obj=cluster)[0]
        payment_addr_recs = clusterlib_utils.create_payment_addr_records(
            f"{temp_template}_addr0",
            f"{temp_template}_addr1",
            cluster_obj=cluster,
            stake_vkey_file=stake_addr_rec.vkey_file,
        )

        # fund payment address
        clusterlib_utils.fund_from_faucet(
            *payment_addr_recs,
            cluster_obj=cluster,
            faucet_data=cluster_manager.cache.addrs_data["user1"],
        )

        pool_user = clusterlib.PoolUser(payment=payment_addr_recs[1],
                                        stake=stake_addr_rec)

        clusterlib_utils.wait_for_epoch_interval(cluster_obj=cluster,
                                                 start=5,
                                                 stop=-20)
        init_epoch = cluster.get_epoch()

        # submit registration certificate and delegate to pool
        delegation_out = delegation.delegate_stake_addr(
            cluster_obj=cluster,
            addrs_data=cluster_manager.cache.addrs_data,
            temp_template=temp_template,
            pool_user=pool_user,
            pool_id=pool_id,
        )

        assert (
            cluster.get_epoch() == init_epoch
        ), "Delegation took longer than expected and would affect other checks"

        tx_db_deleg = dbsync_utils.check_tx(
            cluster_obj=cluster, tx_raw_output=delegation_out.tx_raw_output)
        if tx_db_deleg:
            # check in db-sync that both payment addresses share single stake address
            assert (dbsync_utils.get_utxo(
                address=payment_addr_recs[0].address).stake_address ==
                    stake_addr_rec.address)
            assert (dbsync_utils.get_utxo(
                address=payment_addr_recs[1].address).stake_address ==
                    stake_addr_rec.address)

        delegation.db_check_delegation(
            pool_user=delegation_out.pool_user,
            db_record=tx_db_deleg,
            deleg_epoch=init_epoch,
            pool_id=delegation_out.pool_id,
        )

        src_address = delegation_out.pool_user.payment.address

        LOGGER.info("Waiting 4 epochs for first reward.")
        cluster.wait_for_new_epoch(new_epochs=4, padding_seconds=10)
        if not cluster.get_stake_addr_info(
                delegation_out.pool_user.stake.address).reward_account_balance:
            pytest.skip(
                f"User of pool '{pool_id}' hasn't received any rewards, cannot continue."
            )

        # make sure we have enough time to finish deregistration in one epoch
        clusterlib_utils.wait_for_epoch_interval(cluster_obj=cluster,
                                                 start=5,
                                                 stop=-40)

        # files for deregistering stake address
        stake_addr_dereg_cert = cluster.gen_stake_addr_deregistration_cert(
            addr_name=f"{temp_template}_addr0",
            stake_vkey_file=delegation_out.pool_user.stake.vkey_file,
        )
        tx_files_deregister = clusterlib.TxFiles(
            certificate_files=[stake_addr_dereg_cert],
            signing_key_files=[
                delegation_out.pool_user.payment.skey_file,
                delegation_out.pool_user.stake.skey_file,
            ],
        )

        # attempt to deregister the stake address - deregistration is expected to fail
        # because there are rewards in the stake address
        with pytest.raises(clusterlib.CLIError) as excinfo:
            cluster.send_tx(
                src_address=src_address,
                tx_name=f"{temp_template}_dereg_fail",
                tx_files=tx_files_deregister,
            )
        assert "StakeKeyNonZeroAccountBalanceDELEG" in str(excinfo.value)

        src_payment_balance = cluster.get_address_balance(src_address)
        reward_balance = cluster.get_stake_addr_info(
            delegation_out.pool_user.stake.address).reward_account_balance

        # withdraw rewards to payment address, deregister stake address
        tx_raw_deregister_output = cluster.send_tx(
            src_address=src_address,
            tx_name=f"{temp_template}_dereg_withdraw",
            tx_files=tx_files_deregister,
            withdrawals=[
                clusterlib.TxOut(
                    address=delegation_out.pool_user.stake.address, amount=-1)
            ],
        )

        # check that the key deposit was returned and rewards withdrawn
        assert (
            cluster.get_address_balance(src_address) == src_payment_balance -
            tx_raw_deregister_output.fee + reward_balance +
            cluster.get_address_deposit()
        ), f"Incorrect balance for source address `{src_address}`"

        # check that the stake address is no longer delegated
        stake_addr_info = cluster.get_stake_addr_info(
            delegation_out.pool_user.stake.address)
        assert (not stake_addr_info.delegation
                ), f"Stake address is still delegated: {stake_addr_info}"

        tx_db_dereg = dbsync_utils.check_tx(
            cluster_obj=cluster, tx_raw_output=tx_raw_deregister_output)
        if tx_db_dereg:
            assert delegation_out.pool_user.stake.address in tx_db_dereg.stake_deregistration
            assert (
                cluster.get_address_balance(src_address) ==
                dbsync_utils.get_utxo(address=src_address).amount_sum
            ), f"Unexpected balance for source address `{src_address}` in db-sync"
Exemple #21
0
    def test_pay_unregistered_stake_addr_from(  # noqa: C901
        self,
        cluster_manager: cluster_management.ClusterManager,
        cluster_pots: clusterlib.ClusterLib,
        pool_users: List[clusterlib.PoolUser],
        fund_src: str,
        addr_history: str,
    ):
        """Send funds from the reserves or treasury pot to unregistered stake address.

        * generate an MIR certificate
        * if a stake address should be known on blockchain:

            - register the stake address
            - if transferring funds from treasury, deregister the stake address
              BEFORE submitting the TX

        * submit a TX with the MIR certificate
        * if a stake address should be known on blockchain and if transferring funds from reserves,
          deregister the stake address AFTER submitting the TX
        * check that the amount was NOT added to the stake address reward account
        * (optional) check transaction in db-sync
        """
        # pylint: disable=too-many-branches
        temp_template = f"{common.get_test_id(cluster_pots)}_{fund_src}"
        cluster = cluster_pots

        if fund_src == self.TREASURY:
            amount = 1_500_000_000_000
            pool_user = pool_users[3]
        else:
            amount = 50_000_000_000_000
            pool_user = pool_users[4]

        init_balance = cluster.get_address_balance(pool_user.payment.address)

        mir_cert = cluster.gen_mir_cert_stake_addr(
            stake_addr=pool_user.stake.address,
            reward=amount,
            tx_name=temp_template,
            use_treasury=fund_src == self.TREASURY,
        )
        tx_files = clusterlib.TxFiles(
            certificate_files=[mir_cert],
            signing_key_files=[
                pool_user.payment.skey_file,
                *cluster.genesis_keys.delegate_skeys,
            ],
        )

        # register the stake address, if it is supposed to be known on blockchain
        if addr_history == "addr_known":
            tx_raw_out_reg = clusterlib_utils.register_stake_address(
                cluster_obj=cluster_pots,
                pool_user=pool_user,
                name_template=temp_template)

            # deregister the stake address before submitting the Tx with MIR cert
            if fund_src == self.TREASURY:
                tx_raw_out_withdrawal, tx_raw_out_dereg = clusterlib_utils.deregister_stake_address(
                    cluster_obj=cluster_pots,
                    pool_user=pool_user,
                    name_template=temp_template)

        # send the transaction at the beginning of an epoch
        if cluster.time_from_epoch_start() > (cluster.epoch_length_sec // 6):
            cluster.wait_for_new_epoch()

        LOGGER.info(
            f"Submitting MIR cert for transferring funds from {fund_src} to "
            f"'{pool_user.stake.address}' in epoch {cluster.get_epoch()} "
            f"on cluster instance {cluster_manager.cluster_instance_num}")
        tx_raw_output = cluster.send_tx(
            src_address=pool_user.payment.address,
            tx_name=temp_template,
            tx_files=tx_files,
        )

        tx_epoch = cluster.get_epoch()

        # deregister the stake address after submitting the Tx with MIR cert
        if addr_history == "addr_known" and fund_src != self.TREASURY:
            tx_raw_out_withdrawal, tx_raw_out_dereg = clusterlib_utils.deregister_stake_address(
                cluster_obj=cluster_pots,
                pool_user=pool_user,
                name_template=temp_template)

        reg_dereg_fees = 0
        if addr_history == "addr_known":
            reg_dereg_fees = tx_raw_out_reg.fee + tx_raw_out_withdrawal.fee + tx_raw_out_dereg.fee

        assert (
            cluster.get_address_balance(
                pool_user.payment.address) == init_balance -
            tx_raw_output.fee - reg_dereg_fees
        ), f"Incorrect balance for source address `{pool_user.payment.address}`"

        tx_db_record = dbsync_utils.check_tx(cluster_obj=cluster,
                                             tx_raw_output=tx_raw_output)
        if tx_db_record:
            if fund_src == self.TREASURY:
                assert tx_db_record.treasury[0].amount == amount, (
                    "Incorrect amount transferred from treasury "
                    f"({tx_db_record.treasury[0].amount} != {amount})")
            else:
                assert tx_db_record.reserve[0].amount == amount, (
                    "Incorrect amount transferred from reserve "
                    f"({tx_db_record.reserve[0].amount} != {amount})")

        # wait for next epoch and check the reward
        cluster.wait_for_new_epoch()

        assert not cluster.get_stake_addr_info(
            pool_user.stake.address
        ).reward_account_balance, (
            f"Reward was added for unregistered stake address `{pool_user.stake.address}`"
        )

        if tx_db_record:
            # check that the amount was not transferred out of the pot
            pots_records = _wait_for_ada_pots(epoch_from=tx_epoch)

            if fund_src == self.TREASURY:
                # normally `treasury[-1]` > `treasury[-2]`
                assert abs(pots_records[-1].treasury -
                           pots_records[-2].treasury) < amount
            else:
                # normally `reserves[-1]` < `reserves[-2]`
                assert abs(pots_records[-2].reserves -
                           pots_records[-1].reserves) < amount
    def test_addr_delegation_deregistration(
        self,
        cluster_and_pool: Tuple[clusterlib.ClusterLib, str],
        pool_users: List[clusterlib.PoolUser],
        pool_users_disposable: List[clusterlib.PoolUser],
        use_build_cmd: bool,
    ):
        """Submit delegation and deregistration certificates in single TX.

        * create stake address registration cert
        * create stake address deregistration cert
        * register stake address
        * create stake address delegation cert
        * delegate and deregister stake address in single TX
        * check that the balance for source address was correctly updated and that the key
          deposit was returned
        * check that the stake address was NOT delegated
        * (optional) check records in db-sync
        """
        cluster, pool_id = cluster_and_pool
        temp_template = f"{common.get_test_id(cluster)}_{use_build_cmd}"

        user_registered = pool_users_disposable[0]
        user_payment = pool_users[0].payment
        src_init_balance = cluster.get_address_balance(user_payment.address)

        # create stake address registration cert
        stake_addr_reg_cert_file = cluster.gen_stake_addr_registration_cert(
            addr_name=f"{temp_template}_addr0",
            stake_vkey_file=user_registered.stake.vkey_file)

        # create stake address deregistration cert
        stake_addr_dereg_cert = cluster.gen_stake_addr_deregistration_cert(
            addr_name=f"{temp_template}_addr0",
            stake_vkey_file=user_registered.stake.vkey_file)

        # register stake address
        tx_files = clusterlib.TxFiles(
            certificate_files=[stake_addr_reg_cert_file],
            signing_key_files=[user_payment.skey_file],
        )
        tx_raw_output_reg = cluster.send_tx(
            src_address=user_payment.address,
            tx_name=f"{temp_template}_reg",
            tx_files=tx_files,
        )

        tx_db_reg = dbsync_utils.check_tx(cluster_obj=cluster,
                                          tx_raw_output=tx_raw_output_reg)
        if tx_db_reg:
            assert user_registered.stake.address in tx_db_reg.stake_registration

        # check that the balance for source address was correctly updated
        assert (
            cluster.get_address_balance(
                user_payment.address) == src_init_balance -
            tx_raw_output_reg.fee - cluster.get_address_deposit()
        ), f"Incorrect balance for source address `{user_payment.address}`"

        src_registered_balance = cluster.get_address_balance(
            user_payment.address)

        # create stake address delegation cert
        stake_addr_deleg_cert_file = cluster.gen_stake_addr_delegation_cert(
            addr_name=f"{temp_template}_addr0",
            stake_vkey_file=user_registered.stake.vkey_file,
            stake_pool_id=pool_id,
        )

        clusterlib_utils.wait_for_epoch_interval(cluster_obj=cluster,
                                                 start=5,
                                                 stop=-20)
        init_epoch = cluster.get_epoch()

        # delegate and deregister stake address in single TX
        tx_files = clusterlib.TxFiles(
            certificate_files=[
                stake_addr_deleg_cert_file, stake_addr_dereg_cert
            ],
            signing_key_files=[
                user_payment.skey_file, user_registered.stake.skey_file
            ],
        )

        if use_build_cmd:
            tx_raw_output_deleg = cluster.build_tx(
                src_address=user_payment.address,
                tx_name=f"{temp_template}_deleg_dereg",
                tx_files=tx_files,
                fee_buffer=2_000_000,
                witness_override=len(tx_files.signing_key_files),
            )
            tx_signed = cluster.sign_tx(
                tx_body_file=tx_raw_output_deleg.out_file,
                signing_key_files=tx_files.signing_key_files,
                tx_name=f"{temp_template}_deleg_dereg",
            )
            cluster.submit_tx(tx_file=tx_signed,
                              txins=tx_raw_output_deleg.txins)
        else:
            tx_raw_output_deleg = cluster.send_tx(
                src_address=user_payment.address,
                tx_name=f"{temp_template}_deleg_dereg",
                tx_files=tx_files,
            )

        # check that the balance for source address was correctly updated and that the key
        # deposit was returned
        assert (
            cluster.get_address_balance(
                user_payment.address) == src_registered_balance -
            tx_raw_output_deleg.fee + cluster.get_address_deposit()
        ), f"Incorrect balance for source address `{user_payment.address}`"

        # check that the stake address was NOT delegated
        stake_addr_info = cluster.get_stake_addr_info(
            user_registered.stake.address)
        assert not stake_addr_info.delegation, f"Stake address was delegated: {stake_addr_info}"

        tx_db_deleg = dbsync_utils.check_tx(cluster_obj=cluster,
                                            tx_raw_output=tx_raw_output_deleg)
        if tx_db_deleg:
            assert user_registered.stake.address in tx_db_deleg.stake_deregistration
            assert user_registered.stake.address == tx_db_deleg.stake_delegation[
                0].address
            assert tx_db_deleg.stake_delegation[
                0].active_epoch_no == init_epoch + 2
            assert pool_id == tx_db_deleg.stake_delegation[0].pool_id
    def test_pay_stake_addr_from(self, cluster: clusterlib.ClusterLib,
                                 registered_user: clusterlib.PoolUser,
                                 fund_src: str):
        """Send funds from the reserves or treasury pot to stake address.

        * generate an MIR certificate
        * submit a TX with the MIR certificate
        * check that the expected amount was added to the stake address reward account
        """
        temp_template = helpers.get_func_name()
        amount = 50_000_000

        init_reward = cluster.get_stake_addr_info(
            registered_user.stake.address).reward_account_balance
        init_balance = cluster.get_address_balance(
            registered_user.payment.address)

        mir_cert = cluster.gen_mir_cert_stake_addr(
            stake_addr=registered_user.stake.address,
            reward=amount,
            tx_name=temp_template,
            use_treasury=fund_src == "treasury",
        )
        tx_files = clusterlib.TxFiles(
            certificate_files=[mir_cert],
            signing_key_files=[
                registered_user.payment.skey_file,
                *cluster.genesis_keys.delegate_skeys,
            ],
        )

        # send the transaction at the beginning of an epoch
        if cluster.time_from_epoch_start() > (cluster.epoch_length_sec // 6):
            cluster.wait_for_new_epoch()

        tx_raw_output = cluster.send_tx(
            src_address=registered_user.payment.address,
            tx_name=temp_template,
            tx_files=tx_files,
        )

        assert (
            cluster.get_address_balance(
                registered_user.payment.address) == init_balance -
            tx_raw_output.fee
        ), f"Incorrect balance for source address `{registered_user.payment.address}`"

        cluster.wait_for_new_epoch()

        assert (
            cluster.get_stake_addr_info(
                registered_user.stake.address).reward_account_balance ==
            init_reward + amount
        ), f"Incorrect reward balance for stake address `{registered_user.stake.address}`"

        tx_db_record = dbsync_utils.check_tx(cluster_obj=cluster,
                                             tx_raw_output=tx_raw_output)
        if tx_db_record:
            if fund_src == "treasury":
                assert tx_db_record.treasury[0].amount == amount, (
                    "Incorrect amount transferred from treasury "
                    f"({tx_db_record.treasury[0].amount} != {amount})")
            else:
                assert tx_db_record.reserve[0].amount == amount, (
                    "Incorrect amount transferred from reserve "
                    f"({tx_db_record.reserve[0].amount} != {amount})")
Exemple #24
0
    def test_transfer_to_reserves(
        self, cluster_pots: clusterlib.ClusterLib, pool_users: List[clusterlib.PoolUser]
    ):
        """Send funds from the treasury pot to the reserves pot.

        Expected to fail when Era < Alonzo.
        """
        temp_template = helpers.get_func_name()
        cluster = cluster_pots
        pool_user = pool_users[0]
        amount = 1_000_000_000_000

        init_balance = cluster.get_address_balance(pool_user.payment.address)

        mir_cert = cluster.gen_mir_cert_to_rewards(transfer=amount, tx_name=temp_template)
        tx_files = clusterlib.TxFiles(
            certificate_files=[mir_cert],
            signing_key_files=[pool_user.payment.skey_file, *cluster.genesis_keys.delegate_skeys],
        )

        # send the transaction at the beginning of an epoch
        if cluster.time_from_epoch_start() > (cluster.epoch_length_sec // 6):
            cluster.wait_for_new_epoch()

        # fail is expected when Era < Alonzo
        if VERSIONS.cluster_era < VERSIONS.ALONZO:
            with pytest.raises(clusterlib.CLIError) as excinfo:
                cluster.send_tx(
                    src_address=pool_user.payment.address,
                    tx_name=temp_template,
                    tx_files=tx_files,
                )
            assert "MIRTransferNotCurrentlyAllowed" in str(excinfo.value)
            return

        tx_raw_output = cluster.send_tx(
            src_address=pool_user.payment.address,
            tx_name=temp_template,
            tx_files=tx_files,
        )

        assert (
            cluster.get_address_balance(pool_user.payment.address)
            == init_balance - tx_raw_output.fee
        ), f"Incorrect balance for source address `{pool_user.payment.address}`"

        tx_db_record = dbsync_utils.check_tx(cluster_obj=cluster, tx_raw_output=tx_raw_output)
        if tx_db_record:
            tx_epoch = cluster.get_epoch()

            assert tx_db_record.pot_transfers[0].treasury == -amount, (
                "Incorrect amount transferred from treasury "
                f"({tx_db_record.pot_transfers[0].treasury} != {-amount})"
            )
            assert tx_db_record.pot_transfers[0].reserves == amount, (
                "Incorrect amount transferred to reserves "
                f"({tx_db_record.pot_transfers[0].reserves} != {amount})"
            )

            cluster.wait_for_new_epoch()

            pots_records = list(dbsync_utils.query_ada_pots(epoch_from=tx_epoch))
            # normally `treasury[-1]` > `treasury[-2]`
            assert pots_records[-1].treasury < pots_records[-2].treasury
            # normally `reserves[-1]` < `reserves[-2]`
            assert pots_records[-1].reserves > pots_records[-2].reserves
def _build_fund_script(
    temp_template: str,
    cluster: clusterlib.ClusterLib,
    payment_addr: clusterlib.AddressRecord,
    dst_addr: clusterlib.AddressRecord,
    plutus_op: plutus_common.PlutusOp,
) -> Tuple[List[clusterlib.UTXOData], List[clusterlib.UTXOData],
           clusterlib.TxRawOutput]:
    """Fund a Plutus script and create the locked UTxO and collateral UTxO.

    Uses `cardano-cli transaction build` command for building the transactions.
    """
    # for mypy
    assert plutus_op.execution_cost
    assert plutus_op.datum_file or plutus_op.datum_value
    assert plutus_op.redeemer_cbor_file

    script_fund = 200_000_000

    script_address = cluster.gen_payment_addr(
        addr_name=temp_template, payment_script_file=plutus_op.script_file)

    redeem_cost = plutus_common.compute_cost(
        execution_cost=plutus_op.execution_cost,
        protocol_params=cluster.get_protocol_params())

    # create a Tx output with a datum hash at the script address

    tx_files = clusterlib.TxFiles(signing_key_files=[payment_addr.skey_file], )

    txouts = [
        clusterlib.TxOut(
            address=script_address,
            amount=script_fund,
            inline_datum_file=plutus_op.datum_file
            if plutus_op.datum_file else "",
            inline_datum_value=plutus_op.datum_value
            if plutus_op.datum_value else "",
        ),
        # for collateral
        clusterlib.TxOut(address=dst_addr.address,
                         amount=redeem_cost.collateral),
    ]

    tx_output = cluster.build_tx(
        src_address=payment_addr.address,
        tx_name=f"{temp_template}_step1",
        tx_files=tx_files,
        txouts=txouts,
        fee_buffer=2_000_000,
        join_txouts=False,
    )
    tx_signed = cluster.sign_tx(
        tx_body_file=tx_output.out_file,
        signing_key_files=tx_files.signing_key_files,
        tx_name=f"{temp_template}_step1",
    )
    cluster.submit_tx(tx_file=tx_signed, txins=tx_output.txins)

    txid = cluster.get_txid(tx_body_file=tx_output.out_file)

    script_utxos = cluster.get_utxo(txin=f"{txid}#1")
    assert script_utxos, "No script UTxO"

    script_utxos_lovelace = [
        u for u in script_utxos if u.coin == clusterlib.DEFAULT_COIN
    ]
    script_lovelace_balance = functools.reduce(lambda x, y: x + y.amount,
                                               script_utxos_lovelace, 0)
    assert (script_lovelace_balance == script_fund
            ), f"Incorrect balance for script address `{script_address}`"

    collateral_utxos = cluster.get_utxo(txin=f"{txid}#2")
    assert collateral_utxos, "No collateral UTxO"

    collateral_utxos_lovelace = [
        u for u in collateral_utxos if u.coin == clusterlib.DEFAULT_COIN
    ]
    collateral_lovelace_balance = functools.reduce(lambda x, y: x + y.amount,
                                                   collateral_utxos_lovelace,
                                                   0)
    assert (collateral_lovelace_balance == redeem_cost.collateral
            ), f"Incorrect balance for collateral address `{dst_addr.address}`"

    dbsync_utils.check_tx(cluster_obj=cluster, tx_raw_output=tx_output)

    return script_utxos, collateral_utxos, tx_output