Skip to content

Commit b95af51

Browse files
authored
Merge pull request #1187 from mkoura/plutusv2_minting_happy
First test for minting using Plutus V2
2 parents 1ddf79e + 20ce009 commit b95af51

File tree

5 files changed

+283
-3
lines changed

5 files changed

+283
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "PlutusScriptV2",
3+
"description": "",
4+
"cborHex": "5907655907620100003232323232323232323232323232332232323232322232325335320193333573466e1cd55cea80124000466442466002006004646464646464646464646464646666ae68cdc39aab9d500c480008cccccccccccc88888888888848cccccccccccc00403403002c02802402001c01801401000c008cd4050054d5d0a80619a80a00a9aba1500b33501401635742a014666aa030eb9405cd5d0a804999aa80c3ae501735742a01066a02803e6ae85401cccd54060081d69aba150063232323333573466e1cd55cea801240004664424660020060046464646666ae68cdc39aab9d5002480008cc8848cc00400c008cd40a9d69aba15002302b357426ae8940088c98c80b4cd5ce01701681589aab9e5001137540026ae854008c8c8c8cccd5cd19b8735573aa004900011991091980080180119a8153ad35742a00460566ae84d5d1280111931901699ab9c02e02d02b135573ca00226ea8004d5d09aba2500223263202933573805405204e26aae7940044dd50009aba1500533501475c6ae854010ccd540600708004d5d0a801999aa80c3ae200135742a004603c6ae84d5d1280111931901299ab9c026025023135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d55cf280089baa00135742a004601c6ae84d5d1280111931900b99ab9c018017015101613263201633573892010350543500016135573ca00226ea800448c88c008dd6000990009aa80a911999aab9f0012500a233500930043574200460066ae880080508c8c8cccd5cd19b8735573aa004900011991091980080180118061aba150023005357426ae8940088c98c8050cd5ce00a80a00909aab9e5001137540024646464646666ae68cdc39aab9d5004480008cccc888848cccc00401401000c008c8c8c8cccd5cd19b8735573aa0049000119910919800801801180a9aba1500233500f014357426ae8940088c98c8064cd5ce00d00c80b89aab9e5001137540026ae854010ccd54021d728039aba150033232323333573466e1d4005200423212223002004357426aae79400c8cccd5cd19b875002480088c84888c004010dd71aba135573ca00846666ae68cdc3a801a400042444006464c6403666ae7007006c06406005c4d55cea80089baa00135742a00466a016eb8d5d09aba2500223263201533573802c02a02626ae8940044d5d1280089aab9e500113754002266aa002eb9d6889119118011bab00132001355012223233335573e0044a010466a00e66442466002006004600c6aae754008c014d55cf280118021aba200301213574200222440042442446600200800624464646666ae68cdc3a800a40004642446004006600a6ae84d55cf280191999ab9a3370ea0049001109100091931900819ab9c01101000e00d135573aa00226ea80048c8c8cccd5cd19b875001480188c848888c010014c01cd5d09aab9e500323333573466e1d400920042321222230020053009357426aae7940108cccd5cd19b875003480088c848888c004014c01cd5d09aab9e500523333573466e1d40112000232122223003005375c6ae84d55cf280311931900819ab9c01101000e00d00c00b135573aa00226ea80048c8c8cccd5cd19b8735573aa004900011991091980080180118029aba15002375a6ae84d5d1280111931900619ab9c00d00c00a135573ca00226ea80048c8cccd5cd19b8735573aa002900011bae357426aae7940088c98c8028cd5ce00580500409baa001232323232323333573466e1d4005200c21222222200323333573466e1d4009200a21222222200423333573466e1d400d2008233221222222233001009008375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c4664424444444660040120106eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc8848888888cc018024020c030d5d0a8049bae357426ae8940248cccd5cd19b875006480088c848888888c01c020c034d5d09aab9e500b23333573466e1d401d2000232122222223005008300e357426aae7940308c98c804ccd5ce00a00980880800780700680600589aab9d5004135573ca00626aae7940084d55cf280089baa0012323232323333573466e1d400520022333222122333001005004003375a6ae854010dd69aba15003375a6ae84d5d1280191999ab9a3370ea0049000119091180100198041aba135573ca00c464c6401866ae700340300280244d55cea80189aba25001135573ca00226ea80048c8c8cccd5cd19b875001480088c8488c00400cdd71aba135573ca00646666ae68cdc3a8012400046424460040066eb8d5d09aab9e500423263200933573801401200e00c26aae7540044dd500089119191999ab9a3370ea00290021091100091999ab9a3370ea00490011190911180180218031aba135573ca00846666ae68cdc3a801a400042444004464c6401466ae7002c02802001c0184d55cea80089baa0012323333573466e1d40052002200723333573466e1d40092000212200123263200633573800e00c00800626aae74dd5000a4c2400292010350543100122002112323001001223300330020020011"
5+
}

cardano_node_tests/tests/plutus_common.py

+10
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
MINTING_WITNESS_REDEEMER_PLUTUS_V1 = SCRIPTS_V1_DIR / "witness-redeemer.plutus"
3030
MINTING_TOKENNAME_PLUTUS_V1 = SCRIPTS_V1_DIR / "mint-tokenname.plutus"
3131

32+
MINTING_PLUTUS_V2 = SCRIPTS_V2_DIR / "anyone-can-mint.plutus"
33+
3234
STAKE_GUESS_42_PLUTUS_V1 = SCRIPTS_V1_DIR / "guess-42-stake.plutus"
3335

3436
REDEEMER_42 = PLUTUS_DIR / "42.redeemer"
@@ -81,6 +83,9 @@ class ExecutionCost(NamedTuple):
8183
)
8284
MINTING_TOKENNAME_COST = ExecutionCost(per_time=230_732_000, per_space=539_860, fixed_cost=47_786)
8385

86+
MINTING_V2_COST = ExecutionCost(per_time=167_089_597, per_space=537_352, fixed_cost=43_053)
87+
MINTING_V2_REF_COST = ExecutionCost(per_time=198_080_433, per_space=633_678, fixed_cost=50_845)
88+
8489

8590
# TODO: cost in old Alonzo cost model
8691
if configuration.ALONZO_COST_MODEL or VERSIONS.cluster_era == VERSIONS.ALONZO:
@@ -131,6 +136,11 @@ class PlutusScriptData(NamedTuple):
131136
),
132137
}
133138

139+
MINTING_PLUTUS = {
140+
"v1": PlutusScriptData(script_file=MINTING_PLUTUS_V1, execution_cost=MINTING_COST),
141+
"v2": PlutusScriptData(script_file=MINTING_PLUTUS_V2, execution_cost=MINTING_V2_COST),
142+
}
143+
134144

135145
class PlutusOp(NamedTuple):
136146
script_file: Path
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
"""Tests for minting with Plutus V2 using `transaction build`."""
2+
import logging
3+
from pathlib import Path
4+
from typing import List
5+
from typing import Optional
6+
from typing import Tuple
7+
8+
import allure
9+
import pytest
10+
from _pytest.fixtures import FixtureRequest
11+
from cardano_clusterlib import clusterlib
12+
13+
from cardano_node_tests.tests import common
14+
from cardano_node_tests.tests import plutus_common
15+
from cardano_node_tests.utils import cluster_management
16+
from cardano_node_tests.utils import clusterlib_utils
17+
from cardano_node_tests.utils import helpers
18+
from cardano_node_tests.utils.versions import VERSIONS
19+
20+
LOGGER = logging.getLogger(__name__)
21+
22+
pytestmark = [
23+
pytest.mark.skipif(
24+
VERSIONS.transaction_era < VERSIONS.BABBAGE,
25+
reason="runs only with Babbage+ TX",
26+
),
27+
pytest.mark.skipif(not common.BUILD_USABLE, reason=common.BUILD_SKIP_MSG),
28+
pytest.mark.smoke,
29+
]
30+
31+
32+
@pytest.fixture
33+
def payment_addrs(
34+
cluster_manager: cluster_management.ClusterManager,
35+
cluster: clusterlib.ClusterLib,
36+
) -> List[clusterlib.AddressRecord]:
37+
"""Create new payment address."""
38+
test_id = common.get_test_id(cluster)
39+
addrs = clusterlib_utils.create_payment_addr_records(
40+
*[f"{test_id}_payment_addr_{i}" for i in range(2)],
41+
cluster_obj=cluster,
42+
)
43+
44+
# fund source address
45+
clusterlib_utils.fund_from_faucet(
46+
addrs[0],
47+
cluster_obj=cluster,
48+
faucet_data=cluster_manager.cache.addrs_data["user1"],
49+
amount=3_000_000_000,
50+
)
51+
52+
return addrs
53+
54+
55+
def _fund_issuer(
56+
cluster_obj: clusterlib.ClusterLib,
57+
temp_template: str,
58+
payment_addr: clusterlib.AddressRecord,
59+
issuer_addr: clusterlib.AddressRecord,
60+
minting_cost: plutus_common.ScriptCost,
61+
amount: int,
62+
reference_script: Optional[Path] = None,
63+
) -> Tuple[
64+
List[clusterlib.UTXOData],
65+
List[clusterlib.UTXOData],
66+
Optional[clusterlib.UTXOData],
67+
clusterlib.TxRawOutput,
68+
]:
69+
"""Fund the token issuer."""
70+
issuer_init_balance = cluster_obj.get_address_balance(issuer_addr.address)
71+
72+
tx_files = clusterlib.TxFiles(
73+
signing_key_files=[payment_addr.skey_file],
74+
)
75+
txouts = [
76+
clusterlib.TxOut(
77+
address=issuer_addr.address,
78+
amount=amount,
79+
),
80+
# for collateral
81+
clusterlib.TxOut(address=issuer_addr.address, amount=minting_cost.collateral),
82+
]
83+
84+
reference_amount = 0
85+
if reference_script:
86+
reference_amount = 10_000_000
87+
# for reference UTxO
88+
txouts.append(
89+
clusterlib.TxOut(
90+
address=issuer_addr.address,
91+
amount=reference_amount,
92+
reference_script_file=reference_script,
93+
)
94+
)
95+
96+
tx_output = cluster_obj.build_tx(
97+
src_address=payment_addr.address,
98+
tx_name=f"{temp_template}_step1",
99+
tx_files=tx_files,
100+
txouts=txouts,
101+
fee_buffer=2_000_000,
102+
# don't join 'change' and 'collateral' txouts, we need separate UTxOs
103+
join_txouts=False,
104+
)
105+
tx_signed = cluster_obj.sign_tx(
106+
tx_body_file=tx_output.out_file,
107+
signing_key_files=tx_files.signing_key_files,
108+
tx_name=f"{temp_template}_step1",
109+
)
110+
cluster_obj.submit_tx(tx_file=tx_signed, txins=tx_output.txins)
111+
112+
issuer_balance = cluster_obj.get_address_balance(issuer_addr.address)
113+
assert (
114+
issuer_balance == issuer_init_balance + amount + minting_cost.collateral + reference_amount
115+
), f"Incorrect balance for token issuer address `{issuer_addr.address}`"
116+
117+
txid = cluster_obj.get_txid(tx_body_file=tx_output.out_file)
118+
mint_utxos = cluster_obj.get_utxo(txin=f"{txid}#1")
119+
collateral_utxos = cluster_obj.get_utxo(txin=f"{txid}#2")
120+
121+
reference_utxo = None
122+
if reference_script:
123+
reference_utxos = cluster_obj.get_utxo(txin=f"{txid}#3")
124+
assert reference_utxos, "No reference script UTxO"
125+
reference_utxo = reference_utxos[0]
126+
127+
return mint_utxos, collateral_utxos, reference_utxo, tx_output
128+
129+
130+
@pytest.mark.testnets
131+
class TestBuildMinting:
132+
"""Tests for minting using Plutus smart contracts and `transaction build`."""
133+
134+
@allure.link(helpers.get_vcs_link())
135+
@pytest.mark.parametrize(
136+
"use_reference_script", (True, False), ids=("reference_script", "script_file")
137+
)
138+
def test_minting_one_token(
139+
self,
140+
cluster: clusterlib.ClusterLib,
141+
payment_addrs: List[clusterlib.AddressRecord],
142+
use_reference_script: bool,
143+
request: FixtureRequest,
144+
):
145+
"""Test minting a token with a Plutus script.
146+
147+
Uses `cardano-cli transaction build` command for building the transactions.
148+
149+
* fund the token issuer and create a UTxO for collateral and possibly reference script
150+
* check that the expected amount was transferred to token issuer's address
151+
* mint the token using a Plutus script
152+
* check that the token was minted and collateral UTxO was not spent
153+
* check expected fees
154+
* check expected Plutus cost
155+
"""
156+
# pylint: disable=too-many-locals
157+
temp_template = f"{common.get_test_id(cluster)}_{request.node.callspec.id}"
158+
payment_addr = payment_addrs[0]
159+
issuer_addr = payment_addrs[1]
160+
161+
lovelace_amount = 2_000_000
162+
token_amount = 5
163+
script_fund = 200_000_000
164+
165+
minting_cost = plutus_common.compute_cost(
166+
execution_cost=plutus_common.MINTING_V2_COST,
167+
protocol_params=cluster.get_protocol_params(),
168+
)
169+
170+
issuer_init_balance = cluster.get_address_balance(issuer_addr.address)
171+
172+
# Step 1: fund the token issuer and create UTxOs for collaterals and reference script
173+
174+
mint_utxos, collateral_utxos, reference_utxo, tx_output_step1 = _fund_issuer(
175+
cluster_obj=cluster,
176+
temp_template=temp_template,
177+
payment_addr=payment_addr,
178+
issuer_addr=issuer_addr,
179+
minting_cost=minting_cost,
180+
amount=script_fund,
181+
reference_script=plutus_common.MINTING_PLUTUS_V2 if use_reference_script else None,
182+
)
183+
assert reference_utxo or not use_reference_script, "No reference script UTxO"
184+
185+
# Step 2: mint the "qacoin"
186+
187+
policyid = cluster.get_policyid(plutus_common.MINTING_PLUTUS_V2)
188+
asset_name = f"qacoin{clusterlib.get_rand_str(4)}".encode("utf-8").hex()
189+
token = f"{policyid}.{asset_name}"
190+
mint_txouts = [
191+
clusterlib.TxOut(address=issuer_addr.address, amount=token_amount, coin=token)
192+
]
193+
194+
plutus_mint_data = [
195+
clusterlib.Mint(
196+
txouts=mint_txouts,
197+
script_file=plutus_common.MINTING_PLUTUS_V2 if not use_reference_script else "",
198+
reference_txin=reference_utxo if use_reference_script else None,
199+
collaterals=collateral_utxos,
200+
redeemer_file=plutus_common.REDEEMER_42,
201+
policyid=policyid,
202+
)
203+
]
204+
205+
tx_files_step2 = clusterlib.TxFiles(
206+
signing_key_files=[issuer_addr.skey_file],
207+
)
208+
txouts_step2 = [
209+
clusterlib.TxOut(address=issuer_addr.address, amount=lovelace_amount),
210+
*mint_txouts,
211+
]
212+
tx_output_step2 = cluster.build_tx(
213+
src_address=payment_addr.address,
214+
tx_name=f"{temp_template}_step2",
215+
tx_files=tx_files_step2,
216+
txins=mint_utxos,
217+
txouts=txouts_step2,
218+
mint=plutus_mint_data,
219+
)
220+
plutus_cost = cluster.calculate_plutus_script_cost(
221+
src_address=payment_addr.address,
222+
tx_name=f"{temp_template}_step2",
223+
tx_files=tx_files_step2,
224+
txins=mint_utxos,
225+
txouts=txouts_step2,
226+
mint=plutus_mint_data,
227+
)
228+
tx_signed_step2 = cluster.sign_tx(
229+
tx_body_file=tx_output_step2.out_file,
230+
signing_key_files=tx_files_step2.signing_key_files,
231+
tx_name=f"{temp_template}_step2",
232+
)
233+
cluster.submit_tx(tx_file=tx_signed_step2, txins=mint_utxos)
234+
235+
assert cluster.get_address_balance(
236+
issuer_addr.address
237+
) == issuer_init_balance + minting_cost.collateral + lovelace_amount + (
238+
reference_utxo.amount if reference_utxo else 0
239+
), f"Incorrect balance for token issuer address `{issuer_addr.address}`"
240+
241+
token_utxo = cluster.get_utxo(address=issuer_addr.address, coins=[token])
242+
assert token_utxo and token_utxo[0].amount == token_amount, "The token was NOT minted"
243+
244+
# check that reference UTxO was NOT spent
245+
assert not reference_utxo or cluster.get_utxo(
246+
txin=f"{reference_utxo.utxo_hash}#{reference_utxo.utxo_ix}"
247+
), "Reference UTxO was spent"
248+
249+
# check expected fees
250+
if use_reference_script:
251+
expected_fee_step1 = 252_929
252+
expected_fee_step2 = 230_646
253+
expected_cost = plutus_common.MINTING_V2_REF_COST
254+
else:
255+
expected_fee_step1 = 167_437
256+
expected_fee_step2 = 304_694
257+
expected_cost = plutus_common.MINTING_V2_COST
258+
259+
assert helpers.is_in_interval(tx_output_step2.fee, expected_fee_step2, frac=0.15)
260+
assert helpers.is_in_interval(tx_output_step1.fee, expected_fee_step1, frac=0.15)
261+
262+
plutus_common.check_plutus_cost(
263+
plutus_cost=plutus_cost,
264+
expected_cost=[expected_cost],
265+
)

nix/cardano-clusterlib.nix

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
buildPythonPackage rec {
44
pname = "cardano-clusterlib";
5-
version = "0.3.0rc4";
5+
version = "0.3.0rc5";
66
src = fetchPypi {
77
inherit pname version;
8-
sha256 = "cPcz/eyypbtoZLZ5FThTjdBD993tLdyFouGFb5put3k=";
8+
sha256 = "6RN/OvapNejKCl5y5h5Gr57g3Xkq2PI1IC0hLNNnZw8=";
99
};
1010
doCheck = false;
1111
nativeBuildInputs = [ setuptools_scm ];

setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ setup_requires =
3232
setuptools_scm
3333
install_requires =
3434
allure-pytest
35-
cardano-clusterlib >= 0.3.0rc4,<0.4.0
35+
cardano-clusterlib >= 0.3.0rc5,<0.4.0
3636
cbor2
3737
filelock
3838
hypothesis

0 commit comments

Comments
 (0)