Skip to content

Commit ea84fa0

Browse files
authored
Merge pull request #20 from cffls/plutus
Plutus functionalities
2 parents fe6849a + efa03ac commit ea84fa0

File tree

12 files changed

+491
-81
lines changed

12 files changed

+491
-81
lines changed

integration-test/configs/local-alonzo/alonzo-genesis.json

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
{
22
"collateralPercentage": 1,
3-
"maxBlockExUnits": {
4-
"exUnitsMem": 500000000000,
5-
"exUnitsSteps": 500000000000
6-
},
73
"maxCollateralInputs": 5,
84
"maxValueSize": 4000,
95
"costModels": {
@@ -177,12 +173,24 @@
177173
}
178174
},
179175
"executionPrices": {
180-
"prMem": 0.1,
181-
"prSteps": 0.1
176+
"prSteps":
177+
{
178+
"numerator" : 721,
179+
"denominator" : 10000000
180+
},
181+
"prMem":
182+
{
183+
"numerator" : 577,
184+
"denominator" : 10000
185+
}
182186
},
183187
"lovelacePerUTxOWord": 1,
184188
"maxTxExUnits": {
185-
"exUnitsMem": 500000000000,
186-
"exUnitsSteps": 500000000000
189+
"exUnitsMem": 10000000,
190+
"exUnitsSteps": 10000000000
191+
},
192+
"maxBlockExUnits": {
193+
"exUnitsMem": 50000000,
194+
"exUnitsSteps": 40000000000
187195
}
188196
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
59082459082101000033232332233223232333222323332223233333333222222223233322232333322223232332232333222323332223232332233223232333332222233223322332233223322332222223232533530313330063333573466e1cd55ce9baa0054800081248c98d4c120cd5ce0260248238231bad0043333573466e1cd55cea8012400046601264646464646464646464646666ae68cdc39aab9d500a480008cccccccccc05ccd40948c8c8cccd5cd19b8735573aa004900011980e981c1aba15002302a357426ae8940088c98d4c160cd5ce02e02c82b82b09aab9e5001137540026ae854028cd4094098d5d0a804999aa8163ae502b35742a010666aa058eb940acd5d0a80399a8128209aba15006335025335505204a75a6ae854014c8c8c8cccd5cd19b8735573aa0049000119a80f9919191999ab9a3370e6aae7540092000233502733504075a6ae854008c114d5d09aba25002232635305c3357380c00ba0b60b426aae7940044dd50009aba150023232323333573466e1cd55cea80124000466a04a66a080eb4d5d0a80118229aba135744a004464c6a60b866ae7018017416c1684d55cf280089baa001357426ae8940088c98d4c160cd5ce02e02c82b82b09aab9e5001137540026ae854010cd4095d71aba15003335025335505275c40026ae854008c0dcd5d09aba2500223263530543357380b00aa0a60a426ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba150023232323333573466e1d400520062301c3039357426aae79400c8cccd5cd19b875002480108c06cc10cd5d09aab9e500423333573466e1d400d20022301b302e357426aae7940148cccd5cd19b875004480008c078dd71aba135573ca00c464c6a609e66ae7014c14013813413012c1284d55cea80089baa001357426ae8940088c98d4c120cd5ce026024823823082409931a982399ab9c49010350543500048046135573ca00226ea80048848cc00400c0088004888888888848cccccccccc00402c02802402001c01801401000c00880048848cc00400c008800448848cc00400c0084800448848cc00400c0084800448848cc00400c00848004848888c010014848888c00c014848888c008014848888c004014800448c88c008dd6000990009aa81a911999aab9f0012500e233500d30043574200460066ae880080cc8c8c8c8cccd5cd19b8735573aa006900011998039919191999ab9a3370e6aae754009200023300d303135742a00466a02605a6ae84d5d1280111931a981b99ab9c03b038036035135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba25002232635303333573806e06806406226ae8940044d55cf280089baa00122212333001004003002200122123300100300220011335500175ceb44488c88c008dd5800990009aa81791191999aab9f0022500923350083355031300635573aa004600a6aae794008c010d5d100181709aba10011122002122122330010040031200112232323333573466e1d400520002350083005357426aae79400c8cccd5cd19b87500248008940208c98d4c0a8cd5ce01701581481401389aab9d500113754002242446004006224400224002464646666ae68cdc39aab9d5002480008cc018c01cd5d0a8011bad357426ae8940088c98d4c090cd5ce01401281181109aab9e50011375400244246600200600440024646666ae68cdc39aab9d5001480008dd71aba135573ca004464c6a604066ae7009008407c0784dd500089119191999ab9a3370ea00290021280391999ab9a3370ea004900111a80518031aba135573ca00846666ae68cdc3a801a40004a014464c6a604666ae7009c09008808408007c4d55cea80089baa00112122230030041122200211222001120012323333573466e1d40052002200623333573466e1d400920002006232635301b33573803e03803403203026aae74dd50008910010910009000919191919191999ab9a3370ea0029006100591999ab9a3370ea0049005100691999ab9a3370ea00690041198059bae35742a00a6eb4d5d09aba2500523333573466e1d4011200623300d375c6ae85401cdd71aba135744a00e46666ae68cdc3a802a400846602460286ae854024dd71aba135744a01246666ae68cdc3a8032400446028602a6ae84d55cf280591999ab9a3370ea00e900011809980b1aba135573ca018464c6a604066ae7009008407c07807407006c0680640604d55cea80209aab9e5003135573ca00426aae7940044dd500090911111118038041109111111198030048041091111111802804091111110020911111100191091111111980100480411091111111980080480410009191919191999ab9a3370ea002900111998041bad35742a0086eb4d5d0a8019bad357426ae89400c8cccd5cd19b875002480008c028c02cd5d09aab9e5006232635301133573802a02402001e01c26aae75400c4d5d1280089aab9e5001137540024244600400644424466600200a0080064002464646666ae68cdc3a800a40044600c6eb8d5d09aab9e500323333573466e1d4009200023008375c6ae84d55cf280211931a980599ab9c00f00c00a009008135573aa00226ea80048488c00800c8488c00400c800444888c8c8cccd5cd19b8735573aa0049000119aa80598031aba150023005357426ae8940088c98d4c020cd5ce00600480380309aab9e5001137540029309000900088910919800801801089000a4903505431001123230010012233003300200200133322222253353004333573466e1c00920540060051006133573892010e77726f6e672072656465656d65720000512200212200120011

integration-test/test/test_mint_nft.py renamed to integration-test/test/test_all.py

Lines changed: 169 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,43 @@
55
import tempfile
66
import time
77

8+
import cbor2
89
from retry import retry
910

1011
from pycardano import *
1112

1213

13-
class TestMintNFT:
14+
@retry(tries=10, delay=6)
15+
def check_chain_context(chain_context):
16+
print(f"Current chain tip: {chain_context.last_block_slot}")
17+
18+
19+
class TestAll:
1420
# Define chain context
1521
NETWORK = Network.TESTNET
1622

1723
OGMIOS_WS = "ws://localhost:1337"
1824

1925
chain_context = OgmiosChainContext(OGMIOS_WS, Network.TESTNET)
2026

21-
@retry(tries=10, delay=6)
22-
def check_ogmios(self):
23-
print(f"Current chain tip: {self.chain_context.last_block_slot}")
27+
check_chain_context(chain_context)
2428

25-
def test_mint(self):
26-
self.check_ogmios()
27-
chain_context = OgmiosChainContext(self.OGMIOS_WS, Network.TESTNET)
28-
29-
payment_key_path = os.environ.get("PAYMENT_KEY")
30-
extended_key_path = os.environ.get("EXTENDED_PAYMENT_KEY")
31-
if not payment_key_path or not extended_key_path:
32-
raise Exception(
33-
"Cannot find payment key. Please specify environment variable PAYMENT_KEY and extended_key_path"
34-
)
35-
payment_skey = PaymentSigningKey.load(payment_key_path)
36-
payment_vkey = PaymentVerificationKey.from_signing_key(payment_skey)
37-
extended_payment_skey = PaymentExtendedSigningKey.load(extended_key_path)
38-
extended_payment_vkey = PaymentExtendedVerificationKey.from_signing_key(
39-
extended_payment_skey
29+
payment_key_path = os.environ.get("PAYMENT_KEY")
30+
extended_key_path = os.environ.get("EXTENDED_PAYMENT_KEY")
31+
if not payment_key_path or not extended_key_path:
32+
raise Exception(
33+
"Cannot find payment key. Please specify environment variable PAYMENT_KEY and extended_key_path"
4034
)
41-
address = Address(payment_vkey.hash(), network=self.NETWORK)
35+
payment_skey = PaymentSigningKey.load(payment_key_path)
36+
payment_vkey = PaymentVerificationKey.from_signing_key(payment_skey)
37+
extended_payment_skey = PaymentExtendedSigningKey.load(extended_key_path)
38+
extended_payment_vkey = PaymentExtendedVerificationKey.from_signing_key(
39+
extended_payment_skey
40+
)
41+
42+
@retry(tries=2, delay=6)
43+
def test_mint(self):
44+
address = Address(self.payment_vkey.hash(), network=self.NETWORK)
4245

4346
# Load payment keys or create them if they don't exist
4447
def load_or_create_key_pair(base_dir, base_name):
@@ -74,10 +77,10 @@ def load_or_create_key_pair(base_dir, base_name):
7477
pub_key_policy_1 = ScriptPubkey(policy_vkey.hash())
7578

7679
# A policy that requires a signature from the extended payment key
77-
pub_key_policy_2 = ScriptPubkey(extended_payment_vkey.hash())
80+
pub_key_policy_2 = ScriptPubkey(self.extended_payment_vkey.hash())
7881

7982
# A time policy that disallows token minting after 10000 seconds from last block
80-
must_before_slot = InvalidHereAfter(chain_context.last_block_slot + 10000)
83+
must_before_slot = InvalidHereAfter(self.chain_context.last_block_slot + 10000)
8184

8285
# Combine two policies using ScriptAll policy
8386
policy = ScriptAll([pub_key_policy_1, pub_key_policy_2, must_before_slot])
@@ -125,7 +128,7 @@ def load_or_create_key_pair(base_dir, base_name):
125128
"""Build transaction"""
126129

127130
# Create a transaction builder
128-
builder = TransactionBuilder(chain_context)
131+
builder = TransactionBuilder(self.chain_context)
129132

130133
# Add our own address as the input address
131134
builder.add_input_address(address)
@@ -143,7 +146,7 @@ def load_or_create_key_pair(base_dir, base_name):
143146
builder.auxiliary_data = auxiliary_data
144147

145148
# Calculate the minimum amount of lovelace that need to hold the NFT we are going to mint
146-
min_val = min_lovelace(Value(0, my_nft), chain_context)
149+
min_val = min_lovelace(Value(0, my_nft), self.chain_context)
147150

148151
# Send the NFT to our own address
149152
nft_output = TransactionOutput(address, Value(min_val, my_nft))
@@ -154,19 +157,21 @@ def load_or_create_key_pair(base_dir, base_name):
154157

155158
"""Sign transaction and add witnesses"""
156159
# Sign the transaction body hash using the payment signing key
157-
payment_signature = payment_skey.sign(tx_body.hash())
160+
payment_signature = self.payment_skey.sign(tx_body.hash())
158161

159162
# Sign the transaction body hash using the extended payment signing key
160-
extended_payment_signature = extended_payment_skey.sign(tx_body.hash())
163+
extended_payment_signature = self.extended_payment_skey.sign(tx_body.hash())
161164

162165
# Sign the transaction body hash using the policy signing key because we are minting new tokens
163166
policy_signature = policy_skey.sign(tx_body.hash())
164167

165168
# Add verification keys and their signatures to the witness set
166169
vk_witnesses = [
167-
VerificationKeyWitness(payment_vkey, payment_signature),
170+
VerificationKeyWitness(self.payment_vkey, payment_signature),
168171
VerificationKeyWitness(policy_vkey, policy_signature),
169-
VerificationKeyWitness(extended_payment_vkey, extended_payment_signature),
172+
VerificationKeyWitness(
173+
self.extended_payment_vkey, extended_payment_signature
174+
),
170175
]
171176

172177
# Create final signed transaction
@@ -185,11 +190,11 @@ def load_or_create_key_pair(base_dir, base_name):
185190

186191
# Submit signed transaction to the network
187192
print("############### Submitting transaction ###############")
188-
chain_context.submit_tx(signed_tx.to_cbor())
193+
self.chain_context.submit_tx(signed_tx.to_cbor())
189194

190195
time.sleep(3)
191196

192-
utxos = chain_context.utxos(str(address))
197+
utxos = self.chain_context.utxos(str(address))
193198
found_nft = False
194199

195200
for utxo in utxos:
@@ -207,7 +212,7 @@ def load_or_create_key_pair(base_dir, base_name):
207212
),
208213
)
209214

210-
builder = TransactionBuilder(chain_context)
215+
builder = TransactionBuilder(self.chain_context)
211216
builder.add_input_address(address)
212217

213218
builder.add_output(nft_to_send)
@@ -216,11 +221,11 @@ def load_or_create_key_pair(base_dir, base_name):
216221

217222
"""Sign transaction and add witnesses"""
218223
# Sign the transaction body hash using the payment signing key
219-
payment_signature = payment_skey.sign(tx_body.hash())
224+
payment_signature = self.payment_skey.sign(tx_body.hash())
220225

221226
# Add verification keys and their signatures to the witness set
222227
vk_witnesses = [
223-
VerificationKeyWitness(payment_vkey, payment_signature),
228+
VerificationKeyWitness(self.payment_vkey, payment_signature),
224229
]
225230

226231
# Create final signed transaction
@@ -236,11 +241,11 @@ def load_or_create_key_pair(base_dir, base_name):
236241

237242
# Submit signed transaction to the network
238243
print("############### Submitting transaction ###############")
239-
chain_context.submit_tx(signed_tx.to_cbor())
244+
self.chain_context.submit_tx(signed_tx.to_cbor())
240245

241246
time.sleep(3)
242247

243-
utxos = chain_context.utxos(str(address))
248+
utxos = self.chain_context.utxos(str(address))
244249
found_nft = False
245250

246251
for utxo in utxos:
@@ -249,3 +254,132 @@ def load_or_create_key_pair(base_dir, base_name):
249254
found_nft = True
250255

251256
assert found_nft, f"Cannot find target NFT in address: {address}"
257+
258+
@retry(tries=2, delay=6)
259+
def test_plutus(self):
260+
261+
# ----------- Giver give ---------------
262+
263+
with open("plutus_scripts/fortytwo.plutus", "r") as f:
264+
script_hex = f.read()
265+
forty_two_script = cbor2.loads(bytes.fromhex(script_hex))
266+
267+
script_hash = plutus_script_hash(forty_two_script)
268+
269+
script_address = Address(script_hash, network=self.NETWORK)
270+
271+
giver_address = Address(self.payment_vkey.hash(), network=self.NETWORK)
272+
273+
builder = TransactionBuilder(self.chain_context)
274+
builder.add_input_address(giver_address)
275+
datum = PlutusData() # A Unit type "()" in Haskell
276+
builder.add_output(
277+
TransactionOutput(script_address, 50000000, datum_hash=datum_hash(datum))
278+
)
279+
280+
tx_body = builder.build(change_address=giver_address)
281+
witness_set = builder.build_witness_set()
282+
283+
payment_signature = self.payment_skey.sign(tx_body.hash())
284+
285+
witness_set.vkey_witnesses = [
286+
VerificationKeyWitness(self.payment_vkey, payment_signature),
287+
]
288+
289+
signed_tx = Transaction(
290+
tx_body,
291+
witness_set,
292+
)
293+
294+
print("############### Transaction created ###############")
295+
print(signed_tx)
296+
print(signed_tx.to_cbor())
297+
print("############### Submitting transaction ###############")
298+
self.chain_context.submit_tx(signed_tx.to_cbor())
299+
time.sleep(3)
300+
301+
# ----------- Fund taker a collateral UTxO ---------------
302+
303+
taker_address = Address(self.extended_payment_vkey.hash(), network=self.NETWORK)
304+
305+
builder = TransactionBuilder(self.chain_context)
306+
307+
builder.add_input_address(giver_address)
308+
builder.add_output(TransactionOutput(taker_address, 5000000))
309+
310+
tx_body = builder.build(change_address=giver_address)
311+
witness_set = builder.build_witness_set()
312+
313+
payment_signature = self.payment_skey.sign(tx_body.hash())
314+
315+
witness_set.vkey_witnesses = [
316+
VerificationKeyWitness(self.payment_vkey, payment_signature),
317+
]
318+
319+
signed_tx = Transaction(
320+
tx_body,
321+
witness_set,
322+
)
323+
324+
print("############### Transaction created ###############")
325+
print(signed_tx)
326+
print(signed_tx.to_cbor())
327+
print("############### Submitting transaction ###############")
328+
self.chain_context.submit_tx(signed_tx.to_cbor())
329+
time.sleep(3)
330+
331+
# ----------- Taker take ---------------
332+
333+
redeemer = Redeemer(
334+
RedeemerTag.SPEND, 42, ExecutionUnits(mem=10000000, steps=10000000000)
335+
)
336+
337+
utxo_to_spend = self.chain_context.utxos(str(script_address))[0]
338+
339+
taker_address = Address(self.extended_payment_vkey.hash(), network=self.NETWORK)
340+
341+
builder = TransactionBuilder(self.chain_context)
342+
343+
builder.add_script_input(utxo_to_spend, forty_two_script, datum, redeemer)
344+
take_output = TransactionOutput(taker_address, 25123456)
345+
builder.add_output(take_output)
346+
347+
non_nft_utxo = None
348+
for utxo in self.chain_context.utxos(str(taker_address)):
349+
if isinstance(utxo.output.amount, int):
350+
non_nft_utxo = utxo
351+
break
352+
353+
builder.collaterals.append(non_nft_utxo)
354+
355+
tx_body = builder.build(change_address=taker_address)
356+
witness_set = builder.build_witness_set()
357+
358+
collateral_signature = self.extended_payment_skey.sign(tx_body.hash())
359+
360+
witness_set.vkey_witnesses = [
361+
VerificationKeyWitness(self.extended_payment_vkey, collateral_signature),
362+
]
363+
364+
signed_tx = Transaction(
365+
tx_body,
366+
witness_set,
367+
)
368+
369+
print("############### Transaction created ###############")
370+
print(signed_tx)
371+
print(signed_tx.to_cbor())
372+
print("############### Submitting transaction ###############")
373+
self.chain_context.submit_tx(signed_tx.to_cbor())
374+
375+
time.sleep(3)
376+
377+
utxos = self.chain_context.utxos(str(taker_address))
378+
found = False
379+
380+
for utxo in utxos:
381+
output = utxo.output
382+
if output == take_output:
383+
found = True
384+
385+
assert found, f"Cannot find target UTxO in address: {taker_address}"

pycardano/exception.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ class InvalidTransactionException(PyCardanoException):
3434
pass
3535

3636

37+
class TransactionBuilderException(PyCardanoException):
38+
pass
39+
40+
3741
class TransactionFailedException(PyCardanoException):
3842
pass
3943

pycardano/hash.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""All type of hashes in Cardano ledger spec."""
22

3-
from typing import Type, TypeVar, Union
3+
from typing import TypeVar, Union
44

55
from pycardano.serialization import CBORSerializable
66

@@ -10,6 +10,7 @@
1010
"TRANSACTION_HASH_SIZE",
1111
"DATUM_HASH_SIZE",
1212
"AUXILIARY_DATA_HASH_SIZE",
13+
"SCRIPT_DATA_HASH_SIZE",
1314
"ConstrainedBytes",
1415
"VerificationKeyHash",
1516
"ScriptHash",
@@ -63,7 +64,7 @@ def to_primitive(self) -> bytes:
6364
return self.payload
6465

6566
@classmethod
66-
def from_primitive(cls: Type[T], value: Union[bytes, str]) -> T:
67+
def from_primitive(cls: T, value: Union[bytes, str]) -> T:
6768
if isinstance(value, str):
6869
value = bytes.fromhex(value)
6970
return cls(value)

0 commit comments

Comments
 (0)