Exemple #1
0
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_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)
Exemple #3
0
    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 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)
Exemple #5
0
    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)
Exemple #6
0
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
Exemple #7
0
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_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}`"
Exemple #9
0
    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}`"
Exemple #10
0
    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 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,
        )
Exemple #12
0
    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
Exemple #13
0
    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(
    *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)
Exemple #15
0
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
    def test_lock_tx_datum_as_witness(
        self, cluster: clusterlib.ClusterLib, payment_addrs: List[clusterlib.AddressRecord]
    ):
        """Test unlock a Tx output with a datum as witness.

        Expect failure.
        """
        __: Any  # mypy workaround
        temp_template = common.get_test_id(cluster)
        amount = 2_000_000

        plutus_op = PLUTUS_OP_ALWAYS_SUCCEEDS

        # for mypy
        assert plutus_op.execution_cost
        assert plutus_op.datum_file
        assert plutus_op.redeemer_cbor_file

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

        script_utxos, collateral_utxos, __, __ = _fund_script(
            temp_template=temp_template,
            cluster=cluster,
            payment_addr=payment_addrs[0],
            dst_addr=payment_addrs[1],
            plutus_op=plutus_op,
            amount=amount,
            redeem_cost=redeem_cost,
            use_inline_datum=True,
        )

        plutus_txins = [
            clusterlib.ScriptTxIn(
                txins=script_utxos,
                script_file=plutus_op.script_file,
                collaterals=collateral_utxos,
                execution_units=(
                    plutus_op.execution_cost.per_time,
                    plutus_op.execution_cost.per_space,
                ),
                redeemer_cbor_file=plutus_op.redeemer_cbor_file,
                datum_file=plutus_op.datum_file,
            )
        ]

        tx_files_redeem = clusterlib.TxFiles(
            signing_key_files=[payment_addrs[1].skey_file],
        )
        txouts_redeem = [
            clusterlib.TxOut(address=payment_addrs[1].address, amount=amount),
        ]

        tx_output_redeem = cluster.build_raw_tx_bare(
            out_file=f"{temp_template}_step2_tx.body",
            txouts=txouts_redeem,
            tx_files=tx_files_redeem,
            fee=redeem_cost.fee + FEE_REDEEM_TXSIZE,
            script_txins=plutus_txins,
        )

        tx_signed_redeem = cluster.sign_tx(
            tx_body_file=tx_output_redeem.out_file,
            signing_key_files=tx_files_redeem.signing_key_files,
            tx_name=f"{temp_template}_step2",
        )

        with pytest.raises(clusterlib.CLIError) as excinfo:
            cluster.submit_tx(
                tx_file=tx_signed_redeem,
                txins=[t.txins[0] for t in tx_output_redeem.script_txins if t.txins],
            )
        err_str = str(excinfo.value)
        assert "NonOutputSupplimentaryDatums" in err_str, err_str
    def test_txout_locking(
        self,
        cluster: clusterlib.ClusterLib,
        payment_addrs: List[clusterlib.AddressRecord],
        use_inline_datum: bool,
        use_reference_script: bool,
        request: FixtureRequest,
    ):
        """Test combinations of inline datum and datum file + reference script and script file.

        * create the necessary Tx outputs
        * check that the expected amount was locked at the script address
        * spend the locked UTxO
        * check that the expected UTxOs were correctly spent
        """
        temp_template = f"{common.get_test_id(cluster)}_{request.node.callspec.id}"
        amount = 2_000_000

        plutus_op = PLUTUS_OP_ALWAYS_SUCCEEDS

        # for mypy
        assert plutus_op.execution_cost
        assert plutus_op.datum_file
        assert plutus_op.redeemer_cbor_file

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

        # Step 1: fund the Plutus script

        script_utxos, collateral_utxos, reference_utxos, __ = _fund_script(
            temp_template=temp_template,
            cluster=cluster,
            payment_addr=payment_addrs[0],
            dst_addr=payment_addrs[1],
            plutus_op=plutus_op,
            amount=amount,
            redeem_cost=redeem_cost,
            use_inline_datum=use_inline_datum,
            use_reference_script=use_reference_script,
        )

        plutus_txins = [
            clusterlib.ScriptTxIn(
                txins=script_utxos,
                script_file=plutus_op.script_file if not use_reference_script else "",
                reference_txin=reference_utxos[0] if use_reference_script else None,
                reference_type=clusterlib.ScriptTypes.PLUTUS_V2 if use_reference_script else "",
                collaterals=collateral_utxos,
                execution_units=(
                    plutus_op.execution_cost.per_time,
                    plutus_op.execution_cost.per_space,
                ),
                redeemer_cbor_file=plutus_op.redeemer_cbor_file,
                inline_datum_present=use_inline_datum,
                datum_file=plutus_op.datum_file if not use_inline_datum else "",
            )
        ]

        tx_files_redeem = clusterlib.TxFiles(
            signing_key_files=[payment_addrs[1].skey_file],
        )
        txouts_redeem = [
            clusterlib.TxOut(address=payment_addrs[1].address, amount=amount),
        ]

        tx_output_redeem = cluster.build_raw_tx_bare(
            out_file=f"{temp_template}_step2_tx.body",
            txouts=txouts_redeem,
            tx_files=tx_files_redeem,
            fee=redeem_cost.fee + FEE_REDEEM_TXSIZE,
            script_txins=plutus_txins,
        )
        tx_signed_redeem = cluster.sign_tx(
            tx_body_file=tx_output_redeem.out_file,
            signing_key_files=tx_files_redeem.signing_key_files,
            tx_name=f"{temp_template}_step2",
        )

        dst_init_balance = cluster.get_address_balance(payment_addrs[1].address)

        cluster.submit_tx(
            tx_file=tx_signed_redeem,
            txins=[t.txins[0] for t in tx_output_redeem.script_txins if t.txins],
        )

        assert (
            cluster.get_address_balance(payment_addrs[1].address) == dst_init_balance + amount
        ), f"Incorrect balance for destination address `{payment_addrs[1].address}`"

        script_utxos_lovelace = [u for u in script_utxos if u.coin == clusterlib.DEFAULT_COIN]
        for u in script_utxos_lovelace:
            assert not cluster.get_utxo(
                txin=f"{u.utxo_hash}#{u.utxo_ix}", coins=[clusterlib.DEFAULT_COIN]
            ), f"Inputs were NOT spent for `{u.address}`"

        if use_reference_script:
            assert cluster.get_utxo(
                txin=f"{reference_utxos[0].utxo_hash}#{reference_utxos[0].utxo_ix}",
                coins=[clusterlib.DEFAULT_COIN],
            ), "Reference input was spent"
def utxodata2txout(utxodata: clusterlib.UTXOData) -> clusterlib.TxOut:
    """Convert `clusterlib.UTXOData` to `clusterlib.TxOut`."""
    return clusterlib.TxOut(address=utxodata.address, amount=utxodata.amount, coin=utxodata.coin)
def mint_or_burn_witness(
    cluster_obj: clusterlib.ClusterLib,
    new_tokens: List[TokenRecord],
    temp_template: str,
    invalid_hereafter: Optional[int] = None,
    invalid_before: Optional[int] = None,
) -> clusterlib.TxRawOutput:
    """Mint or burn tokens, depending on the `amount` value. Sign using witnesses.

    Positive `amount` value means minting, negative means burning.
    """
    _issuers_addrs = [t.issuers_addrs for t in new_tokens]
    issuers_addrs = set(itertools.chain.from_iterable(_issuers_addrs))
    issuers_skey_files = {p.skey_file for p in issuers_addrs}
    src_address = new_tokens[0].token_mint_addr.address

    # create TX body
    tx_files = clusterlib.TxFiles(
        script_files=clusterlib.ScriptFiles(minting_scripts=[t.script for t in new_tokens]),
    )
    mint = [
        clusterlib.TxOut(address=t.token_mint_addr.address, amount=t.amount, coin=t.token)
        for t in new_tokens
    ]
    fee = cluster_obj.calculate_tx_fee(
        src_address=src_address,
        tx_name=temp_template,
        tx_files=tx_files,
        mint=mint,
        # TODO: workaround for https://github.com/input-output-hk/cardano-node/issues/1892
        witness_count_add=len(issuers_skey_files),
    )
    tx_raw_output = cluster_obj.build_raw_tx(
        src_address=src_address,
        tx_name=temp_template,
        tx_files=tx_files,
        fee=fee,
        invalid_hereafter=invalid_hereafter,
        invalid_before=invalid_before,
        mint=mint,
    )

    # create witness file for each required key
    witness_files = [
        cluster_obj.witness_tx(
            tx_body_file=tx_raw_output.out_file,
            witness_name=f"{temp_template}_skey{idx}",
            signing_key_files=[skey],
        )
        for idx, skey in enumerate(issuers_skey_files)
    ]

    # sign TX using witness files
    tx_witnessed_file = cluster_obj.assemble_tx(
        tx_body_file=tx_raw_output.out_file,
        witness_files=witness_files,
        tx_name=temp_template,
    )

    # submit signed TX
    cluster_obj.submit_tx(tx_file=tx_witnessed_file, txins=tx_raw_output.txins)

    return tx_raw_output
        tx_name=f"{name_template}_dereg_stake_addr",
        tx_files=tx_files_deregister,
    )
    return tx_raw_output


def fund_from_genesis(
    *dst_addrs: str,
    cluster_obj: clusterlib.ClusterLib,
    amount: int = 2_000_000,
    tx_name: Optional[str] = None,
    destination_dir: FileType = ".",
) -> None:
    """Send `amount` from genesis addr to all `dst_addrs`."""
    fund_dst = [
        clusterlib.TxOut(address=d, amount=amount)
        for d in dst_addrs
        if cluster_obj.get_address_balance(d) < amount
    ]
    if not fund_dst:
        return

    with helpers.FileLockIfXdist(f"{helpers.get_basetemp()}/{cluster_obj.genesis_utxo_addr}.lock"):
        tx_name = tx_name or helpers.get_timestamped_rand_str()
        tx_name = f"{tx_name}_genesis_funding"
        fund_tx_files = clusterlib.TxFiles(
            signing_key_files=[
                *cluster_obj.genesis_keys.delegate_skeys,
                cluster_obj.genesis_keys.genesis_utxo_skey,
            ]
        )
    def test_deregister(
        self,
        cluster_manager: cluster_management.ClusterManager,
        cluster_and_pool: Tuple[clusterlib.ClusterLib, str],
    ):
        """Deregister stake address.

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

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

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

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

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

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

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

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

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

        src_address = delegation_out.pool_user.payment.address

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

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

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

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

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

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

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

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

        tx_db_dereg = dbsync_utils.check_tx(
            cluster_obj=cluster, tx_raw_output=tx_raw_deregister_output)
        if tx_db_dereg:
            assert delegation_out.pool_user.stake.address in tx_db_dereg.stake_deregistration
            assert (
                cluster.get_address_balance(src_address) ==
                dbsync_utils.get_utxo(address=src_address).amount_sum
            ), f"Unexpected balance for source address `{src_address}` in db-sync"
Exemple #23
0
def _deploy_lobster_nft(
    cluster_obj: clusterlib.ClusterLib,
    temp_template: str,
    issuer_addr: clusterlib.AddressRecord,
    token_utxos: List[clusterlib.UTXOData],
    lobster_nft_token: str,
    nft_amount: int,
    lovelace_amount: int,
) -> Tuple[str, List[clusterlib.UTXOData], clusterlib.TxRawOutput]:
    """Deploy the LobsterNFT token to script address."""
    script_address = cluster_obj.gen_payment_addr(
        addr_name=f"{temp_template}_deploy_nft",
        payment_script_file=LOBSTER_PLUTUS)

    tx_files = clusterlib.TxFiles(signing_key_files=[issuer_addr.skey_file], )
    txouts = [
        clusterlib.TxOut(address=script_address,
                         amount=lovelace_amount,
                         datum_hash=LOBSTER_DATUM_HASH),
        clusterlib.TxOut(
            address=script_address,
            amount=nft_amount,
            coin=lobster_nft_token,
            datum_hash=LOBSTER_DATUM_HASH,
        ),
    ]

    funds_txin = cluster_obj.get_utxo_with_highest_amount(
        address=issuer_addr.address)
    tx_output = cluster_obj.build_tx(
        src_address=issuer_addr.address,
        tx_name=f"{temp_template}_deploy_nft",
        tx_files=tx_files,
        txins=[*token_utxos, funds_txin],
        txouts=txouts,
    )
    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}_deploy_nft",
    )
    cluster_obj.submit_tx(tx_file=tx_signed, txins=token_utxos)

    txid = cluster_obj.get_txid(tx_body_file=tx_output.out_file)
    deployed_token_utxos = cluster_obj.get_utxo(txin=f"{txid}#1")

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

    token_utxo_lobster = [
        u for u in deployed_token_utxos if u.coin == lobster_nft_token
    ][0]
    assert (
        token_utxo_lobster.amount == nft_amount
    ), f"Incorrect token balance for token issuer address `{script_address}`"

    return script_address, deployed_token_utxos, tx_output
Exemple #24
0
def _mint_lobster_nft(
    cluster_obj: clusterlib.ClusterLib,
    temp_template: str,
    issuer_addr: clusterlib.AddressRecord,
    mint_utxos: List[clusterlib.UTXOData],
    collateral_utxos: List[clusterlib.UTXOData],
    nft_amount: int,
    lovelace_amount: int,
) -> Tuple[str, List[clusterlib.UTXOData], clusterlib.TxRawOutput]:
    """Mint the LobsterNFT token."""
    lobster_policyid = cluster_obj.get_policyid(NFT_MINT_PLUTUS)
    asset_name = b"LobsterNFT".hex()
    lobster_nft_token = f"{lobster_policyid}.{asset_name}"

    mint_txouts = [
        clusterlib.TxOut(
            address=issuer_addr.address,
            amount=nft_amount,
            coin=lobster_nft_token,
        )
    ]

    plutus_mint_data = [
        clusterlib.Mint(
            txouts=mint_txouts,
            script_file=NFT_MINT_PLUTUS,
            collaterals=collateral_utxos,
            redeemer_value="[]",
        )
    ]

    tx_files = clusterlib.TxFiles(signing_key_files=[issuer_addr.skey_file], )
    txouts = [
        clusterlib.TxOut(address=issuer_addr.address, amount=lovelace_amount),
        *mint_txouts,
    ]
    tx_output = cluster_obj.build_tx(
        src_address=issuer_addr.address,
        tx_name=f"{temp_template}_mint_nft",
        tx_files=tx_files,
        txins=mint_utxos,
        txouts=txouts,
        mint=plutus_mint_data,
    )
    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}_mint_nft",
    )
    cluster_obj.submit_tx(tx_file=tx_signed, txins=mint_utxos)

    txid = cluster_obj.get_txid(tx_body_file=tx_output.out_file)
    token_utxos = cluster_obj.get_utxo(txin=f"{txid}#1")

    # check expected balances
    token_utxo_lovelace = [
        u for u in token_utxos if u.coin == clusterlib.DEFAULT_COIN
    ][0]
    assert (
        token_utxo_lovelace.amount == lovelace_amount
    ), f"Incorrect Lovelace balance for token issuer address `{issuer_addr.address}`"

    token_utxo_lobster = [
        u for u in token_utxos if u.coin == lobster_nft_token
    ][0]
    assert (
        token_utxo_lobster.amount == nft_amount
    ), f"Incorrect token balance for token issuer address `{issuer_addr.address}`"

    return lobster_nft_token, token_utxos, tx_output
    def test_reference_locking(
        self,
        cluster: clusterlib.ClusterLib,
        payment_addrs: List[clusterlib.AddressRecord],
    ):
        """Test locking a Tx output with a Plutus script and spending the locked UTxO.

        * create a Tx output with an inline datum at the script address
        * create a Tx output with a reference script
        * check that the expected amount was locked at the script address
        * spend the locked UTxO using the reference UTxO
        * check that the expected amount was spent
        * check that the reference UTxO was not spent
        """
        temp_template = common.get_test_id(cluster)
        amount = 2_000_000

        protocol_params = cluster.get_protocol_params()

        plutus_op = plutus_common.PlutusOp(
            script_file=plutus_common.ALWAYS_SUCCEEDS_PLUTUS_V2,
            datum_file=plutus_common.DATUM_42_TYPED,
            redeemer_cbor_file=plutus_common.REDEEMER_42_CBOR,
            execution_cost=plutus_common.ALWAYS_SUCCEEDS_V2_COST,
        )

        # Step 1: fund the Plutus script

        # for mypy
        assert plutus_op.execution_cost
        assert plutus_op.datum_file

        script_address = cluster.gen_payment_addr(
            addr_name=f"{temp_template}_addr",
            payment_script_file=plutus_op.script_file)
        redeem_cost = plutus_common.compute_cost(
            execution_cost=plutus_op.execution_cost,
            protocol_params=protocol_params)

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

        tx_files_fund = clusterlib.TxFiles(
            signing_key_files=[payment_addrs[0].skey_file], )
        txouts_fund = [
            clusterlib.TxOut(
                address=script_address,
                amount=amount + redeem_cost.fee + FEE_REDEEM_TXSIZE,
                inline_datum_file=plutus_op.datum_file,
            ),
            # for reference script
            clusterlib.TxOut(
                address=payment_addrs[1].address,
                amount=2_000_000,
                reference_script_file=plutus_op.script_file,
            ),
            # for collateral
            clusterlib.TxOut(address=payment_addrs[1].address,
                             amount=redeem_cost.collateral),
        ]

        tx_output_fund = cluster.send_tx(
            src_address=payment_addrs[0].address,
            tx_name=f"{temp_template}_step1",
            txouts=txouts_fund,
            tx_files=tx_files_fund,
            # TODO: workaround for https://github.com/input-output-hk/cardano-node/issues/1892
            witness_count_add=2,
            join_txouts=False,
        )

        txid_fund = cluster.get_txid(tx_body_file=tx_output_fund.out_file)
        script_utxos = cluster.get_utxo(txin=f"{txid_fund}#0",
                                        coins=[clusterlib.DEFAULT_COIN])
        reference_utxos = cluster.get_utxo(txin=f"{txid_fund}#1")
        collateral_utxos = cluster.get_utxo(txin=f"{txid_fund}#2")

        assert script_utxos, "No script UTxO"
        assert reference_utxos, "No reference UTxO"
        assert collateral_utxos, "No collateral UTxO"

        assert (
            script_utxos[0].amount == amount + redeem_cost.fee +
            FEE_REDEEM_TXSIZE
        ), f"Incorrect balance for script address `{script_utxos[0].address}`"

        assert (script_utxos[0].inline_datum_hash
                and script_utxos[0].inline_datum
                ), "Inline datum not present on script UTxO"
        assert reference_utxos[
            0].reference_script, "Reference script not present on reference UTxO"

        # Step 2: spend the "locked" UTxO

        # for mypy
        assert plutus_op.execution_cost
        assert plutus_op.datum_file
        assert plutus_op.redeemer_cbor_file

        reference_utxo = reference_utxos[0]

        plutus_txins = [
            clusterlib.ScriptTxIn(
                txins=script_utxos,
                reference_txin=reference_utxo,
                reference_type=clusterlib.ScriptTypes.PLUTUS_V2,
                collaterals=collateral_utxos,
                execution_units=(
                    plutus_op.execution_cost.per_time,
                    plutus_op.execution_cost.per_space,
                ),
                inline_datum_present=True,
                redeemer_cbor_file=plutus_op.redeemer_cbor_file,
            ),
        ]
        tx_files_redeem = clusterlib.TxFiles(
            signing_key_files=[payment_addrs[1].skey_file], )
        txouts_redeem = [
            clusterlib.TxOut(address=payment_addrs[1].address, amount=amount),
        ]
        tx_output_redeem = cluster.build_raw_tx_bare(
            out_file=f"{temp_template}_step2_tx.body",
            txouts=txouts_redeem,
            tx_files=tx_files_redeem,
            fee=redeem_cost.fee + FEE_REDEEM_TXSIZE,
            script_txins=plutus_txins,
        )
        tx_signed_redeem = cluster.sign_tx(
            tx_body_file=tx_output_redeem.out_file,
            signing_key_files=tx_files_redeem.signing_key_files,
            tx_name=f"{temp_template}_step2",
        )

        dst_init_balance = cluster.get_address_balance(
            payment_addrs[1].address)

        cluster.submit_tx(
            tx_file=tx_signed_redeem,
            txins=[
                t.txins[0] for t in tx_output_redeem.script_txins if t.txins
            ],
        )

        assert (
            cluster.get_address_balance(
                payment_addrs[1].address) == dst_init_balance + amount
        ), f"Incorrect balance for destination address `{payment_addrs[1].address}`"

        script_utxos_lovelace = [
            u for u in script_utxos if u.coin == clusterlib.DEFAULT_COIN
        ]
        for u in script_utxos_lovelace:
            assert not cluster.get_utxo(
                txin=f"{u.utxo_hash}#{u.utxo_ix}",
                coins=[clusterlib.DEFAULT_COIN
                       ]), f"Inputs were NOT spent for `{u.address}`"

        assert cluster.get_utxo(
            txin=f"{reference_utxo.utxo_hash}#{reference_utxo.utxo_ix}",
            coins=[clusterlib.DEFAULT_COIN],
        ), "Reference input was spent"
Exemple #26
0
        tx_files=tx_files_deregister,
    )
    cluster_obj.wait_for_new_block(new_blocks=2)
    return tx_raw_output


def fund_from_genesis(
    *dst_addrs: str,
    cluster_obj: clusterlib.ClusterLib,
    amount: int = 2_000_000,
    tx_name: Optional[str] = None,
    destination_dir: FileType = ".",
) -> None:
    """Send `amount` from genesis addr to all `dst_addrs`."""
    fund_dst = [
        clusterlib.TxOut(address=d, amount=amount) for d in dst_addrs
        if cluster_obj.get_address_balance(d) < amount
    ]
    if not fund_dst:
        return

    with helpers.FileLockIfXdist(
            f"{helpers.get_basetemp()}/{cluster_obj.genesis_utxo_addr}.lock"):
        tx_name = tx_name or helpers.get_timestamped_rand_str()
        tx_name = f"{tx_name}_genesis_funding"
        fund_tx_files = clusterlib.TxFiles(signing_key_files=[
            *cluster_obj.genesis_keys.delegate_skeys,
            cluster_obj.genesis_keys.genesis_utxo_skey,
        ])

        cluster_obj.send_funds(
def _fund_script(
    temp_template: str,
    cluster: clusterlib.ClusterLib,
    payment_addr: clusterlib.AddressRecord,
    dst_addr: clusterlib.AddressRecord,
    plutus_op: plutus_common.PlutusOp,
    amount: int,
    redeem_cost: plutus_common.ScriptCost,
    use_reference_script: Optional[bool] = False,
    use_inline_datum: Optional[bool] = False,
) -> Tuple[
    List[clusterlib.UTXOData],
    List[clusterlib.UTXOData],
    List[clusterlib.UTXOData],
    clusterlib.TxRawOutput,
]:
    """Fund a Plutus script and create the locked UTxO, collateral UTxO and reference script."""
    script_address = cluster.gen_payment_addr(
        addr_name=temp_template, payment_script_file=plutus_op.script_file
    )

    # 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=amount + redeem_cost.fee + FEE_REDEEM_TXSIZE,
            inline_datum_file=(
                plutus_op.datum_file if plutus_op.datum_file and use_inline_datum else ""
            ),
            inline_datum_value=(
                plutus_op.datum_value if plutus_op.datum_value and use_inline_datum else ""
            ),
            datum_hash_file=(
                plutus_op.datum_file if plutus_op.datum_file and not use_inline_datum else ""
            ),
            datum_hash_value=(
                plutus_op.datum_value if plutus_op.datum_value and not use_inline_datum else ""
            ),
        ),
        # for collateral
        clusterlib.TxOut(address=dst_addr.address, amount=redeem_cost.collateral),
    ]

    # for reference script
    if use_reference_script:
        txouts.append(
            clusterlib.TxOut(
                address=dst_addr.address,
                amount=amount,
                reference_script_file=plutus_op.script_file,
            )
        )

    tx_raw_output = cluster.send_tx(
        src_address=payment_addr.address,
        tx_name=f"{temp_template}_step1",
        txouts=txouts,
        tx_files=tx_files,
        # TODO: workaround for https://github.com/input-output-hk/cardano-node/issues/1892
        witness_count_add=2,
        join_txouts=False,
    )

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

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

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

    reference_utxos = []
    if use_reference_script:
        reference_utxos = cluster.get_utxo(txin=f"{txid}#2")
        assert reference_utxos, "No reference script UTxO"

    return script_utxos, collateral_utxos, reference_utxos, tx_raw_output
    def test_undelegate(
        self,
        cluster_manager: cluster_management.ClusterManager,
        cluster_and_pool: Tuple[clusterlib.ClusterLib, str],
    ):
        """Undelegate stake address.

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

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

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

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

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

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

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

        src_address = delegation_out.pool_user.payment.address

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

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

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

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

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

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

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

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

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

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

            db_rewards = dbsync_utils.check_address_reward(
                address=delegation_out.pool_user.stake.address,
                epoch_from=init_epoch)
            assert db_rewards
            db_reward_epochs = sorted(r.spendable_epoch
                                      for r in db_rewards.rewards)
            assert db_reward_epochs[0] == init_epoch + 4
            assert this_epoch in db_reward_epochs
Exemple #29
0
    def test_lobster_name(self, cluster: clusterlib.ClusterLib,
                          payment_addrs: List[clusterlib.AddressRecord]):
        """Test the Lobster Challenge.

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

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

        votes_num = 50
        names_num = 1219
        io_random_seed = 42

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

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

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

        # Step 2: mint the LobsterNFT token

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

        # Step 3: deploy the LobsterNFT token to script address

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

        tx_outputs_all = [tx_output_step1, tx_output_step2, tx_output_step3]

        # Step 4: prepare for voting

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

        # Step 5: vote

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

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

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

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

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

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

            tx_outputs_all.append(tx_output_vote)

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

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

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

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

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

        # check transactions in db-sync
        for tx_out_rec in tx_outputs_all:
            dbsync_utils.check_tx(cluster_obj=cluster,
                                  tx_raw_output=tx_out_rec)
Exemple #30
0
    def test_no_reward_unmet_pledge2(
        self,
        cluster_manager: cluster_management.ClusterManager,
        cluster_lock_pool2: clusterlib.ClusterLib,
    ):
        """Check that the stake pool is not receiving rewards when pledge is not met.

        When the pledge is higher than available funds, neither pool owners nor those who
        delegate to that pool receive rewards.

        * delegate stake address
        * wait for first reward
        * withdraw part of the pledge - the funds are lower than what is needed by the stake pool
        * check that NO new rewards were received by those delegating to the pool
        * check that pool owner is also NOT receiving rewards
        * return the pool to the original state - restore pledge funds
        * check that new rewards were received by those delegating to the pool
        * check that pool owner is also receiving rewards
        """
        pool_name = cluster_management.Resources.POOL2
        cluster = cluster_lock_pool2

        pool_rec = cluster_manager.cache.addrs_data[pool_name]
        pool_owner = clusterlib.PoolUser(payment=pool_rec["payment"],
                                         stake=pool_rec["stake"])
        temp_template = common.get_test_id(cluster)

        pool_id = delegation.get_pool_id(
            cluster_obj=cluster,
            addrs_data=cluster_manager.cache.addrs_data,
            pool_name=pool_name)

        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"

        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_name}' hasn't received any rewards, cannot continue."
            )

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

        # load pool data
        loaded_data = clusterlib_utils.load_registered_pool_data(
            cluster_obj=cluster,
            pool_name=f"changed_{pool_name}",
            pool_id=pool_id)

        pledge_amount = loaded_data.pool_pledge // 2

        # withdraw part of the pledge
        destinations = [
            clusterlib.TxOut(address=delegation_out.pool_user.payment.address,
                             amount=pledge_amount)
        ]
        tx_files = clusterlib.TxFiles(
            signing_key_files=[pool_owner.payment.skey_file])
        cluster.send_funds(
            src_address=pool_owner.payment.address,
            destinations=destinations,
            tx_name=f"{temp_template}_withdraw_pledge",
            tx_files=tx_files,
        )

        assert cluster.get_address_balance(
            pool_owner.payment.address
        ) < loaded_data.pool_pledge, (
            f"Pledge still high - pledge: {loaded_data.pool_pledge}, "
            f"funds: {cluster.get_address_balance(pool_owner.payment.address)}"
        )

        cluster.wait_for_new_epoch(4, padding_seconds=30)

        orig_owner_reward = cluster.get_stake_addr_info(
            pool_rec["reward"].address).reward_account_balance
        orig_user_reward = cluster.get_stake_addr_info(
            delegation_out.pool_user.stake.address).reward_account_balance

        cluster.wait_for_new_epoch(3)

        with cluster_manager.restart_on_failure():
            # check that NO new rewards were received by those delegating to the pool
            assert (orig_user_reward == cluster.get_stake_addr_info(
                delegation_out.pool_user.stake.address).reward_account_balance
                    ), "Received unexpected rewards"

            # check that pool owner is also NOT receiving rewards
            assert (orig_owner_reward == cluster.get_stake_addr_info(
                pool_rec["reward"].address).reward_account_balance
                    ), "Pool owner received unexpected rewards"

            # Return the pool to the original state - restore pledge funds.

            # fund user address so it has enough funds for fees etc.
            clusterlib_utils.fund_from_faucet(
                delegation_out.pool_user,
                cluster_obj=cluster,
                faucet_data=cluster_manager.cache.addrs_data["user1"],
                amount=900_000_000,
                force=True,
            )

            # return pledge
            destinations = [
                clusterlib.TxOut(address=pool_owner.payment.address,
                                 amount=pledge_amount + 100_000_000)
            ]
            tx_files = clusterlib.TxFiles(
                signing_key_files=[delegation_out.pool_user.payment.skey_file])
            cluster.send_funds(
                src_address=delegation_out.pool_user.payment.address,
                destinations=destinations,
                tx_name=f"{temp_template}_return_pledge",
                tx_files=tx_files,
            )

            assert (
                cluster.get_address_balance(
                    pool_owner.payment.address) >= loaded_data.pool_pledge
            ), (f"Funds still low - pledge: {loaded_data.pool_pledge}, "
                f"funds: {cluster.get_address_balance(pool_owner.payment.address)}"
                )

            cluster.wait_for_new_epoch(5, padding_seconds=30)

            # check that new rewards were received by those delegating to the pool
            assert (orig_user_reward < cluster.get_stake_addr_info(
                delegation_out.pool_user.stake.address).reward_account_balance
                    ), "New reward was not received by stake address"

            # check that pool owner is also receiving rewards
            assert (orig_owner_reward < cluster.get_stake_addr_info(
                pool_rec["reward"].address).reward_account_balance
                    ), "New reward was not received by pool reward address"