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, )
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})" )
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
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])
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})")
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)
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
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
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})")
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)
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"
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})")
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