def test_delegate_addr_with_wrong_key( self, cluster_and_pool: Tuple[clusterlib.ClusterLib, str], pool_users: List[clusterlib.PoolUser], pool_users_disposable: List[clusterlib.PoolUser], ): """Try to delegate stake address using wrong payment skey. Expect failure. """ cluster, pool_id = cluster_and_pool temp_template = common.get_test_id(cluster) user_registered = pool_users_disposable[0] user_payment = pool_users[0].payment # 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) # register stake address tx_files = clusterlib.TxFiles( certificate_files=[stake_addr_reg_cert_file], signing_key_files=[user_payment.skey_file], ) cluster.send_tx(src_address=user_payment.address, tx_name=f"{temp_template}_reg", tx_files=tx_files) # 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, ) # delegate stake address, use wrong payment skey tx_files = clusterlib.TxFiles( certificate_files=[stake_addr_deleg_cert_file], signing_key_files=[pool_users[1].payment.skey_file], ) with pytest.raises(clusterlib.CLIError) as excinfo: cluster.send_tx( src_address=user_payment.address, tx_name=f"{temp_template}_deleg", tx_files=tx_files, ) assert "MissingVKeyWitnessesUTXOW" in str(excinfo.value)
def deregister_stake_addr( cluster_obj: clusterlib.ClusterLib, pool_user: clusterlib.PoolUser, name_template: str ) -> clusterlib.TxRawOutput: """Deregister stake address.""" # files for deregistering stake address stake_addr_dereg_cert = cluster_obj.gen_stake_addr_deregistration_cert( addr_name=f"{name_template}_addr0_dereg", stake_vkey_file=pool_user.stake.vkey_file ) tx_files_deregister = clusterlib.TxFiles( certificate_files=[stake_addr_dereg_cert], signing_key_files=[pool_user.payment.skey_file, pool_user.stake.skey_file], ) # withdraw rewards to payment address cluster_obj.withdraw_reward( stake_addr_record=pool_user.stake, dst_addr_record=pool_user.payment, tx_name=name_template, ) tx_raw_output = cluster_obj.send_tx( src_address=pool_user.payment.address, tx_name=f"{name_template}_dereg_stake_addr", tx_files=tx_files_deregister, ) return tx_raw_output
def return_funds_to_faucet( *src_addrs: clusterlib.AddressRecord, cluster_obj: clusterlib.ClusterLib, faucet_addr: str, amount: int = -1, tx_name: Optional[str] = None, destination_dir: FileType = ".", ) -> None: """Send `amount` from all `src_addrs` to `faucet_addr`. The amount of "-1" means all available funds. """ tx_name = tx_name or helpers.get_timestamped_rand_str() tx_name = f"{tx_name}_return_funds" with helpers.FileLockIfXdist(f"{helpers.get_basetemp()}/{faucet_addr}.lock"): try: logging.disable(logging.ERROR) for src in src_addrs: fund_dst = [clusterlib.TxOut(address=faucet_addr, amount=amount)] fund_tx_files = clusterlib.TxFiles(signing_key_files=[src.skey_file]) # try to return funds; don't mind if there's not enough funds for fees etc. try: cluster_obj.send_funds( src_address=src.address, destinations=fund_dst, tx_name=tx_name, tx_files=fund_tx_files, destination_dir=destination_dir, ) except Exception: pass finally: logging.disable(logging.NOTSET)
def test_transfer_to_rewards(self, cluster: clusterlib.ClusterLib, pool_user: clusterlib.PoolUser): """Send funds from the treasury pot to the reserves pot. Expected to fail until Alonzo. """ temp_template = helpers.get_func_name() amount = 50_000 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 until 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)
def register_stake_address(cluster_obj: clusterlib.ClusterLib, pool_user: clusterlib.PoolUser, name_template: str) -> clusterlib.TxRawOutput: """Register stake address.""" # files for registering stake address addr_reg_cert = cluster_obj.gen_stake_addr_registration_cert( addr_name=name_template, stake_vkey_file=pool_user.stake.vkey_file, ) tx_files = clusterlib.TxFiles( certificate_files=[addr_reg_cert], signing_key_files=[ pool_user.payment.skey_file, pool_user.stake.skey_file ], ) tx_raw_output = cluster_obj.send_tx( src_address=pool_user.payment.address, tx_name=f"{name_template}_reg_stake_addr", tx_files=tx_files, ) if not cluster_obj.get_stake_addr_info(pool_user.stake.address): raise AssertionError( f"The address {pool_user.stake.address} was not registered.") return tx_raw_output
def test_addr_deregistration_fees( self, cluster: clusterlib.ClusterLib, pool_users: List[clusterlib.PoolUser], addr_fee: Tuple[int, int], ): """Test stake address deregistration fees.""" no_of_addr, expected_fee = addr_fee temp_template = f"{helpers.get_func_name()}_{no_of_addr}" src_address = pool_users[0].payment.address selected_users = pool_users[:no_of_addr] stake_addr_dereg_certs = [ cluster.gen_stake_addr_deregistration_cert( addr_name=f"{temp_template}_addr{i}", stake_vkey_file=p.stake.vkey_file) for i, p in enumerate(selected_users) ] # create TX data tx_files = clusterlib.TxFiles( certificate_files=[*stake_addr_dereg_certs], signing_key_files=[ *[p.payment.skey_file for p in selected_users], *[p.stake.skey_file for p in selected_users], ], ) # calculate TX fee tx_fee = cluster.calculate_tx_fee(src_address=src_address, tx_name=temp_template, tx_files=tx_files) assert helpers.is_in_interval( tx_fee, expected_fee), "Expected fee doesn't match the actual fee"
def withdraw_reward( cluster_obj: clusterlib.ClusterLib, stake_addr_record: clusterlib.AddressRecord, dst_addr_record: clusterlib.AddressRecord, name_template: str, ) -> None: """Withdraw rewards to payment address.""" dst_address = dst_addr_record.address tx_files_withdrawal = clusterlib.TxFiles(signing_key_files=[ dst_addr_record.skey_file, stake_addr_record.skey_file ], ) LOGGER.info(f"Withdrawing rewards for '{stake_addr_record.address}'") try: cluster_obj.send_tx( src_address=dst_address, tx_name=f"rf_{name_template}_reward_withdrawal", tx_files=tx_files_withdrawal, withdrawals=[ clusterlib.TxOut(address=stake_addr_record.address, amount=-1) ], ) except clusterlib.CLIError: return
def test_negative_fee( self, cluster: clusterlib.ClusterLib, payment_addrs: List[clusterlib.AddressRecord], fee: int, ): """Try to send a transaction with negative fee (property-based test). Expect failure. """ temp_template = f"{helpers.get_func_name()}_{helpers.get_timestamped_rand_str()}" src_address = payment_addrs[0].address dst_address = payment_addrs[1].address destinations = [clusterlib.TxOut(address=dst_address, amount=10)] tx_files = clusterlib.TxFiles( signing_key_files=[payment_addrs[0].skey_file]) with pytest.raises(clusterlib.CLIError) as excinfo: cluster.send_funds( src_address=src_address, destinations=destinations, tx_name=temp_template, tx_files=tx_files, fee=fee, ) assert "option --fee: cannot parse value" in str(excinfo.value)
def _from_to_transactions( self, cluster_obj: clusterlib.ClusterLib, tx_name: str, pool_users: List[clusterlib.PoolUser], from_num: int, to_num: int, amount_expected: Tuple[int, int], ): """Check fees for 1 tx from `from_num` payment addresses to `to_num` payment addresses.""" amount, expected_fee = amount_expected src_address = pool_users[0].payment.address # addr1..addr<from_num+1> from_addr_recs = [p.payment for p in pool_users[1 : from_num + 1]] # addr<from_num+1>..addr<from_num+to_num+1> dst_addresses = [ pool_users[i].payment.address for i in range(from_num + 1, from_num + to_num + 1) ] # create TX data _txins = [cluster_obj.get_utxo(address=r.address) for r in from_addr_recs] # flatten the list of lists that is _txins txins = list(itertools.chain.from_iterable(_txins)) txouts = [clusterlib.TxOut(address=addr, amount=amount) for addr in dst_addresses] tx_files = clusterlib.TxFiles(signing_key_files=[r.skey_file for r in from_addr_recs]) # calculate TX fee tx_fee = cluster_obj.calculate_tx_fee( src_address=src_address, tx_name=tx_name, txins=txins, txouts=txouts, tx_files=tx_files ) assert helpers.is_in_interval( tx_fee, expected_fee ), "Expected fee doesn't match the actual fee"
def test_transfer_no_ada( self, cluster: clusterlib.ClusterLib, payment_addrs: List[clusterlib.AddressRecord], new_token: clusterlib_utils.TokenRecord, ): """Try to create an UTxO with just native tokens, no ADA. Expect failure.""" temp_template = helpers.get_func_name() amount = 10 src_address = new_token.token_mint_addr.address dst_address = payment_addrs[2].address destinations = [ clusterlib.TxOut(address=dst_address, amount=amount, coin=new_token.token) ] tx_files = clusterlib.TxFiles( signing_key_files=[new_token.token_mint_addr.skey_file]) with pytest.raises(clusterlib.CLIError) as excinfo: cluster.send_funds( src_address=src_address, destinations=destinations, tx_name=temp_template, tx_files=tx_files, ) assert "OutputTooSmallUTxO" in str(excinfo.value)
def registered_user( self, cluster_manager: cluster_management.ClusterManager, cluster: clusterlib.ClusterLib, pool_user: clusterlib.PoolUser, ) -> clusterlib.PoolUser: """Register pool user's stake address.""" with cluster_manager.cache_fixture() as fixture_cache: if fixture_cache.value: return fixture_cache.value # type: ignore fixture_cache.value = pool_user temp_template = f"test_mir_certs_ci{cluster_manager.cluster_instance}" addr_reg_cert = cluster.gen_stake_addr_registration_cert( addr_name=temp_template, stake_vkey_file=pool_user.stake.vkey_file, ) tx_files = clusterlib.TxFiles( certificate_files=[addr_reg_cert], signing_key_files=[ pool_user.payment.skey_file, pool_user.stake.skey_file ], ) cluster.send_tx(src_address=pool_user.payment.address, tx_name=f"{temp_template}_reg", tx_files=tx_files) assert cluster.get_stake_addr_info( pool_user.stake.address ), f"The address {pool_user.stake.address} was not registered" return pool_user
def test_transfer_tokens( self, cluster: clusterlib.ClusterLib, payment_addrs: List[clusterlib.AddressRecord], new_token: clusterlib_utils.TokenRecord, amount: int, ): """Test sending tokens to payment address. * send tokens from 1 source address to 1 destination address * check expected token balances for both source and destination addresses * check fees in Lovelace """ temp_template = f"{helpers.get_func_name()}_{amount}" amount_lovelace = 10 src_address = new_token.token_mint_addr.address dst_address = payment_addrs[2].address src_init_balance = cluster.get_address_balance(src_address) src_init_balance_token = cluster.get_address_balance( src_address, coin=new_token.token) dst_init_balance_token = cluster.get_address_balance( dst_address, coin=new_token.token) destinations = [ clusterlib.TxOut(address=dst_address, amount=amount, coin=new_token.token), clusterlib.TxOut(address=dst_address, amount=amount_lovelace), ] tx_files = clusterlib.TxFiles( signing_key_files=[new_token.token_mint_addr.skey_file]) tx_raw_output = cluster.send_funds( src_address=src_address, destinations=destinations, tx_name=temp_template, tx_files=tx_files, ) cluster.wait_for_new_block(new_blocks=2) assert ( cluster.get_address_balance( src_address, coin=new_token.token) == src_init_balance_token - amount ), f"Incorrect token balance for source address `{src_address}`" assert ( cluster.get_address_balance(src_address) == src_init_balance - tx_raw_output.fee - amount_lovelace ), f"Incorrect Lovelace balance for source address `{src_address}`" assert ( cluster.get_address_balance( dst_address, coin=new_token.token) == dst_init_balance_token + amount ), f"Incorrect token balance for destination address `{dst_address}`"
def _create_pool_certificates( self, cluster_obj: clusterlib.ClusterLib, pool_owners: List[clusterlib.PoolUser], temp_template: str, pool_data: clusterlib.PoolData, ) -> Tuple[str, clusterlib.TxFiles]: """Create certificates for registering a stake pool, delegating stake address.""" # create node VRF key pair node_vrf = cluster_obj.gen_vrf_key_pair(node_name=pool_data.pool_name) # create node cold key pair and counter node_cold = cluster_obj.gen_cold_key_pair_and_counter( node_name=pool_data.pool_name) # create stake address registration certs stake_addr_reg_cert_files = [ cluster_obj.gen_stake_addr_registration_cert( addr_name=f"{temp_template}_addr{i}", stake_vkey_file=p.stake.vkey_file) for i, p in enumerate(pool_owners) ] # create stake address delegation cert stake_addr_deleg_cert_files = [ cluster_obj.gen_stake_addr_delegation_cert( addr_name=f"{temp_template}_addr{i}", stake_vkey_file=p.stake.vkey_file, cold_vkey_file=node_cold.vkey_file, ) for i, p in enumerate(pool_owners) ] # create stake pool registration cert pool_reg_cert_file = cluster_obj.gen_pool_registration_cert( pool_data=pool_data, vrf_vkey_file=node_vrf.vkey_file, cold_vkey_file=node_cold.vkey_file, owner_stake_vkey_files=[p.stake.vkey_file for p in pool_owners], ) src_address = pool_owners[0].payment.address # register and delegate stake address, create and register pool tx_files = clusterlib.TxFiles( certificate_files=[ pool_reg_cert_file, *stake_addr_reg_cert_files, *stake_addr_deleg_cert_files, ], signing_key_files=[ *[p.payment.skey_file for p in pool_owners], *[p.stake.skey_file for p in pool_owners], node_cold.skey_file, ], ) return src_address, tx_files
def mint_or_burn_sign( cluster_obj: clusterlib.ClusterLib, new_tokens: List[TokenRecord], temp_template: str, ) -> clusterlib.TxRawOutput: """Mint or burn tokens, depending on the `amount` value. Sign using skeys. Positive `amount` value means minting, negative means burning. """ _issuers_addrs = [n.issuers_addrs for n in new_tokens] issuers_addrs = list(itertools.chain.from_iterable(_issuers_addrs)) issuers_skey_files = [p.skey_file for p in issuers_addrs] token_mint_addr_skey_files = [ t.token_mint_addr.skey_file for t in new_tokens ] src_address = new_tokens[0].token_mint_addr.address tx_files = clusterlib.TxFiles( signing_key_files=[*issuers_skey_files, *token_mint_addr_skey_files]) # build and sign a transaction ttl = cluster_obj.calculate_tx_ttl() fee = cluster_obj.calculate_tx_fee( src_address=src_address, tx_name=temp_template, tx_files=tx_files, ttl=ttl, # TODO: workaround for https://github.com/input-output-hk/cardano-node/issues/1892 witness_count_add=len(issuers_skey_files) * 2, ) tx_raw_output = cluster_obj.build_raw_tx( src_address=src_address, tx_name=temp_template, tx_files=tx_files, fee=fee, ttl=ttl, mint=[ clusterlib.TxOut(address=n.token_mint_addr.address, amount=n.amount, coin=n.token) for n in new_tokens ], ) out_file_signed = cluster_obj.sign_tx( tx_body_file=tx_raw_output.out_file, signing_key_files=tx_files.signing_key_files, tx_name=temp_template, script_files=[n.script for n in new_tokens], ) # submit signed transaction cluster_obj.submit_tx(out_file_signed) cluster_obj.wait_for_new_block(new_blocks=2) return tx_raw_output
def test_delegate_unknown_addr( self, cluster_and_pool: Tuple[clusterlib.ClusterLib, str], pool_users: List[clusterlib.PoolUser], pool_users_disposable: List[clusterlib.PoolUser], use_build_cmd: bool, ): """Try to delegate unknown stake address. Expect failure. """ 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 # 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, ) # delegate unknown stake address tx_files = clusterlib.TxFiles( certificate_files=[stake_addr_deleg_cert_file], signing_key_files=[ user_payment.skey_file, user_registered.stake.skey_file ], ) with pytest.raises(clusterlib.CLIError) as excinfo: if use_build_cmd: tx_raw_output = cluster.build_tx( src_address=user_payment.address, tx_name=f"{temp_template}_deleg_unknown", 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.out_file, signing_key_files=tx_files.signing_key_files, tx_name=f"{temp_template}_deleg_unknown", ) cluster.submit_tx(tx_file=tx_signed, txins=tx_raw_output.txins) else: cluster.send_tx( src_address=user_payment.address, tx_name=f"{temp_template}_deleg_unknown", tx_files=tx_files, ) assert "StakeDelegationImpossibleDELEG" in str(excinfo.value)
def _fund_issuer( cluster_obj: clusterlib.ClusterLib, temp_template: str, payment_addr: clusterlib.AddressRecord, issuer_addr: clusterlib.AddressRecord, amount: int, collateral_amount: int, ) -> Tuple[List[clusterlib.UTXOData], List[clusterlib.UTXOData], clusterlib.TxRawOutput]: """Fund the token issuer.""" issuer_init_balance = cluster_obj.get_address_balance(issuer_addr.address) tx_files = clusterlib.TxFiles(signing_key_files=[payment_addr.skey_file], ) txouts = [ clusterlib.TxOut( address=issuer_addr.address, amount=amount, ), clusterlib.TxOut(address=issuer_addr.address, amount=collateral_amount), ] tx_output = cluster_obj.build_tx( src_address=payment_addr.address, tx_name=f"{temp_template}_step1", tx_files=tx_files, txouts=txouts, fee_buffer=2_000_000, # don't join 'change' and 'collateral' txouts, we need separate UTxOs join_txouts=False, ) tx_signed = cluster_obj.sign_tx( tx_body_file=tx_output.out_file, signing_key_files=tx_files.signing_key_files, tx_name=f"{temp_template}_step1", ) cluster_obj.submit_tx(tx_file=tx_signed, txins=tx_output.txins) issuer_balance = cluster_obj.get_address_balance(issuer_addr.address) assert ( issuer_balance == issuer_init_balance + amount + collateral_amount ), f"Incorrect balance for token issuer address `{issuer_addr.address}`" txid = cluster_obj.get_txid(tx_body_file=tx_output.out_file) mint_utxos = cluster_obj.get_utxo(txin=f"{txid}#1") collateral_utxos = [ clusterlib.UTXOData(utxo_hash=txid, utxo_ix=2, amount=collateral_amount, address=issuer_addr.address) ] return mint_utxos, collateral_utxos, tx_output
def test_exceed_pay_stake_addr_from( self, cluster_pots: clusterlib.ClusterLib, registered_users: List[clusterlib.PoolUser], fund_src: str, ): """Try to send more funds than available from the reserves or treasury pot to stake address. Expect failure. * generate an MIR certificate * submit a TX with the MIR certificate * check that submitting the transaction fails with an expected error """ temp_template = f"{common.get_test_id(cluster_pots)}_{fund_src}" cluster = cluster_pots amount = 30_000_000_000_000_000 registered_user = registered_users[0] 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() with pytest.raises(clusterlib.CLIError) as excinfo: cluster.send_tx( src_address=registered_user.payment.address, tx_name=temp_template, tx_files=tx_files, ) assert "InsufficientForInstantaneousRewardsDELEG" in str(excinfo.value) assert ( cluster.get_address_balance( registered_user.payment.address) == init_balance ), f"Incorrect balance for source address `{registered_user.payment.address}`"
def test_normal_tx_to_script_addr( self, cluster: clusterlib.ClusterLib, payment_addrs: List[clusterlib.AddressRecord]): """Send funds to script address using TX signed with skeys (not using witness files).""" temp_template = helpers.get_func_name() src_address = payment_addrs[0].address amount = 1000 # create multisig script multisig_script = cluster.build_multisig_script( script_name=temp_template, script_type_arg=clusterlib.MultiSigTypeArgs.ALL, payment_vkey_files=[p.vkey_file for p in payment_addrs], ) # create script address script_address = cluster.gen_script_addr(addr_name=temp_template, script_file=multisig_script) # record initial balances src_init_balance = cluster.get_address_balance(src_address) dst_init_balance = cluster.get_address_balance(script_address) # send funds to script address destinations = [ clusterlib.TxOut(address=script_address, amount=amount) ] tx_files = clusterlib.TxFiles( signing_key_files=[payment_addrs[0].skey_file]) tx_raw_output = cluster.send_funds( src_address=src_address, tx_name=temp_template, destinations=destinations, tx_files=tx_files, ) cluster.wait_for_new_block(new_blocks=2) # check final balances assert ( cluster.get_address_balance(src_address) == src_init_balance - tx_raw_output.fee - amount), f"Incorrect balance for source address `{src_address}`" assert ( cluster.get_address_balance(script_address) == dst_init_balance + amount ), f"Incorrect balance for destination address `{script_address}`"
def test_expected_or_higher_fee( self, cluster: clusterlib.ClusterLib, payment_addrs: List[clusterlib.AddressRecord], fee_add: int, ): """Send a transaction with fee that is same or higher than expected.""" temp_template = f"{common.get_test_id(cluster)}_{fee_add}" amount = 2_000_000 src_address = payment_addrs[0].address dst_address = payment_addrs[1].address src_init_balance = cluster.get_address_balance(src_address) dst_init_balance = cluster.get_address_balance(dst_address) destinations = [clusterlib.TxOut(address=dst_address, amount=amount)] tx_files = clusterlib.TxFiles(signing_key_files=[payment_addrs[0].skey_file]) fee = ( cluster.calculate_tx_fee( src_address=src_address, tx_name=temp_template, txouts=destinations, tx_files=tx_files, ) + fee_add ) tx_raw_output = cluster.send_funds( src_address=src_address, destinations=destinations, tx_name=temp_template, tx_files=tx_files, fee=fee, ) assert tx_raw_output.fee == fee, "The actual fee doesn't match the specified fee" assert ( cluster.get_address_balance(src_address) == src_init_balance - tx_raw_output.fee - len(destinations) * amount ), f"Incorrect balance for source address `{src_address}`" assert ( cluster.get_address_balance(dst_address) == dst_init_balance + amount ), f"Incorrect balance for destination address `{dst_address}`"
def test_deregister_not_registered_addr( self, cluster: clusterlib.ClusterLib, pool_users: List[clusterlib.PoolUser], pool_users_disposable: List[clusterlib.PoolUser], use_build_cmd: bool, ): """Deregister not registered stake address.""" temp_template = f"{common.get_test_id(cluster)}_{use_build_cmd}" user_registered = pool_users_disposable[0] user_payment = pool_users[0].payment # files for deregistering stake address stake_addr_dereg_cert = cluster.gen_stake_addr_deregistration_cert( addr_name=f"{temp_template}_addr0", stake_vkey_file=user_registered.stake.vkey_file) tx_files = clusterlib.TxFiles( certificate_files=[stake_addr_dereg_cert], signing_key_files=[ user_payment.skey_file, user_registered.stake.skey_file ], ) with pytest.raises(clusterlib.CLIError) as excinfo: if use_build_cmd: tx_raw_output = cluster.build_tx( src_address=user_payment.address, tx_name=f"{temp_template}_dereg_fail", 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.out_file, signing_key_files=tx_files.signing_key_files, tx_name=f"{temp_template}_dereg_fail", ) cluster.submit_tx(tx_file=tx_signed, txins=tx_raw_output.txins) else: cluster.send_tx( src_address=user_payment.address, tx_name=f"{temp_template}_dereg_fail", tx_files=tx_files, ) assert "StakeKeyNotRegisteredDELEG" in str(excinfo.value)
def update_params_build( cluster_obj: clusterlib.ClusterLib, src_addr_record: clusterlib.AddressRecord, update_proposals: List[UpdateProposal], ) -> None: """Update params using update proposal. Uses `cardano-cli transaction build` command for building the transactions. """ if not update_proposals: return _cli_args = [(u.arg, str(u.value)) for u in update_proposals] cli_args = list(itertools.chain.from_iterable(_cli_args)) temp_template = helpers.get_timestamped_rand_str() # assumption is update proposals are submitted near beginning of epoch epoch = cluster_obj.get_epoch() out_file = cluster_obj.gen_update_proposal( cli_args=cli_args, epoch=epoch, tx_name=temp_template, ) tx_files = clusterlib.TxFiles( proposal_files=[out_file], signing_key_files=[ *cluster_obj.genesis_keys.delegate_skeys, Path(src_addr_record.skey_file), ], ) tx_output = cluster_obj.build_tx( src_address=src_addr_record.address, tx_name=f"{temp_template}_submit_proposal", tx_files=tx_files, fee_buffer=2000_000, ) tx_signed = cluster_obj.sign_tx( tx_body_file=tx_output.out_file, signing_key_files=tx_files.signing_key_files, tx_name=f"{temp_template}_submit_proposal", ) cluster_obj.submit_tx(tx_file=tx_signed, txins=tx_output.txins) LOGGER.info(f"Update Proposal submitted ({cli_args})")
def test_tx_script_invalid(self, cluster: clusterlib.ClusterLib, payment_addrs: List[clusterlib.AddressRecord]): """Build transaction with invalid auxiliary script. Expect failure. """ temp_template = helpers.get_func_name() tx_files = clusterlib.TxFiles( signing_key_files=[payment_addrs[0].skey_file], script_files=[self.JSON_METADATA_FILE], # not valid script file ) with pytest.raises(clusterlib.CLIError) as excinfo: cluster.send_tx(src_address=payment_addrs[0].address, tx_name=temp_template, tx_files=tx_files) assert 'Error in $: key "type" not found' in str(excinfo.value)
def test_shelley_cddl(self, cluster: clusterlib.ClusterLib, payment_addrs: List[clusterlib.AddressRecord]): """Check expected failure when Shelley Tx is used with CDDL format.""" temp_template = common.get_test_id(cluster) src_address = payment_addrs[0].address dst_address = payment_addrs[1].address # amount value -1 means all available funds destinations = [clusterlib.TxOut(address=dst_address, amount=-1)] tx_files = clusterlib.TxFiles( signing_key_files=[payment_addrs[1].skey_file]) fee = cluster.calculate_tx_fee( src_address=src_address, tx_name=temp_template, txouts=destinations, tx_files=tx_files, ) orig_cddl_value = cluster.use_cddl try: cluster.use_cddl = True tx_raw_output = cluster.build_raw_tx( src_address=src_address, tx_name=temp_template, txouts=destinations, tx_files=tx_files, fee=fee, ) finally: cluster.use_cddl = orig_cddl_value with pytest.raises(clusterlib.CLIError) as excinfo: cluster.sign_tx( tx_body_file=tx_raw_output.out_file, signing_key_files=tx_files.signing_key_files, tx_name=temp_template, ) if "TextEnvelope error" in str(excinfo.value): pytest.xfail("TextEnvelope error") else: pytest.fail(f"Unexpected error:\n{excinfo.value}")
def test_tx_script_metadata_cbor( self, cluster: clusterlib.ClusterLib, payment_addrs: List[clusterlib.AddressRecord]): """Send transaction with auxiliary script and metadata CBOR. Check that the auxiliary script is present in the TX body. """ temp_template = helpers.get_func_name() payment_vkey_files = [p.vkey_file for p in payment_addrs] # create multisig script multisig_script = cluster.build_multisig_script( script_name=temp_template, script_type_arg=clusterlib.MultiSigTypeArgs.AT_LEAST, payment_vkey_files=payment_vkey_files, required=2, slot=1000, slot_type_arg=clusterlib.MultiSlotTypeArgs.BEFORE, ) tx_files = clusterlib.TxFiles( signing_key_files=[payment_addrs[0].skey_file], metadata_cbor_files=[self.CBOR_METADATA_FILE], script_files=[multisig_script], ) tx_raw_output = cluster.send_tx(src_address=payment_addrs[0].address, tx_name=temp_template, tx_files=tx_files) cluster.wait_for_new_block(new_blocks=2) assert tx_raw_output.fee, "Transaction had no fee" with open(tx_raw_output.out_file) as body_fp: tx_body_json = json.load(body_fp) cbor_body = bytes.fromhex(tx_body_json["cborHex"]) cbor_body_metadata = cbor2.loads(cbor_body)[1] cbor_body_script = cbor_body_metadata[1] assert cbor_body_script, "Auxiliary script not present"
def test_smaller_fee( self, cluster: clusterlib.ClusterLib, payment_addrs: List[clusterlib.AddressRecord], fee_change: float, ): """Try to send a transaction with smaller-than-expected fee. Expect failure. """ temp_template = f"{common.get_test_id(cluster)}_{fee_change}" src_address = payment_addrs[0].address dst_address = payment_addrs[1].address destinations = [clusterlib.TxOut(address=dst_address, amount=10)] tx_files = clusterlib.TxFiles(signing_key_files=[payment_addrs[0].skey_file]) fee = 0.0 if fee_change: fee = ( cluster.calculate_tx_fee( src_address=src_address, tx_name=temp_template, txouts=destinations, tx_files=tx_files, ) / fee_change ) with pytest.raises(clusterlib.CLIError) as excinfo: cluster.send_funds( src_address=src_address, destinations=destinations, tx_name=temp_template, tx_files=tx_files, fee=int(fee), ) assert "FeeTooSmallUTxO" in str(excinfo.value)
def return_funds_to_faucet( cluster_obj: clusterlib.ClusterLib, src_addr: clusterlib.AddressRecord, faucet_address: str, tx_name: str, ) -> None: """Send funds from `src_addr` to `faucet_address`.""" tx_name = f"rf_{tx_name}_return_funds" # the amount of "-1" means all available funds. fund_dst = [clusterlib.TxOut(address=faucet_address, amount=-1)] fund_tx_files = clusterlib.TxFiles(signing_key_files=[src_addr.skey_file]) txins = cluster_obj.get_utxo(address=src_addr.address, coins=[clusterlib.DEFAULT_COIN]) utxos_balance = functools.reduce(lambda x, y: x + y.amount, txins, 0) # skip if there no (or too little) Lovelace if utxos_balance < 1000_000: return # if the balance is too low, add a faucet UTxO so there's enough funds for fee # and the total amount is higher than min ADA value if utxos_balance < 3000_000: faucet_utxos = cluster_obj.get_utxo(address=faucet_address, coins=[clusterlib.DEFAULT_COIN]) futxo = random.choice(faucet_utxos) txins.append(futxo) LOGGER.info(f"Returning funds from '{src_addr.address}'") # try to return funds; don't mind if there's not enough funds for fees etc. with contextlib.suppress(clusterlib.CLIError): cluster_obj.send_tx( src_address=src_addr.address, tx_name=tx_name, txins=txins, txouts=fund_dst, tx_files=fund_tx_files, verify_tx=False, )
def _mint_tx( self, cluster_obj: clusterlib.ClusterLib, new_tokens: List[clusterlib_utils.TokenRecord], temp_template: str, ) -> Path: """Return signed TX for minting new token. Sign using skeys.""" _issuers_addrs = [n.issuers_addrs for n in new_tokens] issuers_addrs = list(itertools.chain.from_iterable(_issuers_addrs)) issuers_skey_files = [p.skey_file for p in issuers_addrs] token_mint_addr_skey_files = [ t.token_mint_addr.skey_file for t in new_tokens ] src_address = new_tokens[0].token_mint_addr.address tx_files = clusterlib.TxFiles(signing_key_files=[ *issuers_skey_files, *token_mint_addr_skey_files ]) # build and sign a transaction tx_raw_output = cluster_obj.build_raw_tx( src_address=src_address, tx_name=temp_template, tx_files=tx_files, fee=100_000, mint=[ clusterlib.TxOut(address=n.token_mint_addr.address, amount=n.amount, coin=n.token) for n in new_tokens ], ) out_file_signed = cluster_obj.sign_tx( tx_body_file=tx_raw_output.out_file, signing_key_files=tx_files.signing_key_files, tx_name=temp_template, script_files=[n.script for n in new_tokens], ) return out_file_signed
def return_funds_to_faucet( *src_addrs: clusterlib.AddressRecord, cluster_obj: clusterlib.ClusterLib, faucet_addr: str, amount: Union[int, List[int]] = -1, tx_name: Optional[str] = None, destination_dir: FileType = ".", ) -> None: """Send `amount` from all `src_addrs` to `faucet_addr`. The amount of "-1" means all available funds. """ tx_name = tx_name or helpers.get_timestamped_rand_str() tx_name = f"{tx_name}_return_funds" if isinstance(amount, int): amount = [amount] * len(src_addrs) with locking.FileLockIfXdist( f"{temptools.get_basetemp()}/{faucet_addr}.lock"): try: logging.disable(logging.ERROR) for addr, amount_rec in zip(src_addrs, amount): fund_dst = [ clusterlib.TxOut(address=faucet_addr, amount=amount_rec) ] fund_tx_files = clusterlib.TxFiles( signing_key_files=[addr.skey_file]) # try to return funds; don't mind if there's not enough funds for fees etc. with contextlib.suppress(Exception): cluster_obj.send_funds( src_address=addr.address, destinations=fund_dst, tx_name=tx_name, tx_files=fund_tx_files, destination_dir=destination_dir, ) finally: logging.disable(logging.NOTSET)
def _withdraw_rewards( *pool_users: clusterlib.PoolUser, cluster_obj: clusterlib.ClusterLib, tx_name: str, ) -> clusterlib.TxRawOutput: """Withdraw rewards from multiple stake addresses to corresponding payment addresses.""" src_addr = pool_users[0].payment tx_files_withdrawal = clusterlib.TxFiles(signing_key_files=[ src_addr.skey_file, *[p.stake.skey_file for p in pool_users] ], ) tx_raw_withdrawal_output = cluster_obj.send_tx( src_address=src_addr.address, tx_name=f"{tx_name}_reward_withdrawals", tx_files=tx_files_withdrawal, withdrawals=[ clusterlib.TxOut(address=p.stake.address, amount=-1) for p in pool_users ], ) return tx_raw_withdrawal_output
def return_funds_to_faucet( cluster_obj: clusterlib.ClusterLib, src_addr: clusterlib.AddressRecord, faucet_addr: str, tx_name: str, ) -> None: """Send funds from `src_addr` to `faucet_addr`.""" tx_name = f"rf_{tx_name}_return_funds" # the amount of "-1" means all available funds. fund_dst = [clusterlib.TxOut(address=faucet_addr, amount=-1)] fund_tx_files = clusterlib.TxFiles(signing_key_files=[src_addr.skey_file]) LOGGER.info(f"Returning funds from '{src_addr.address}'") # try to return funds; don't mind if there's not enough funds for fees etc. try: cluster_obj.send_funds( src_address=src_addr.address, destinations=fund_dst, tx_name=tx_name, tx_files=fund_tx_files, ) except clusterlib.CLIError: pass