Skip to content

Commit b485784

Browse files
authored
Merge pull request #144 from ImperatorLang/eopsin_guide
Switch the Plutus introduction to eopsin
2 parents 08f7829 + 4b534b5 commit b485784

File tree

1 file changed

+73
-19
lines changed

1 file changed

+73
-19
lines changed

docs/source/guides/plutus.rst

+73-19
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,51 @@ Similarly, redeemer can be serialized like following::
7070
Example - FortyTwo
7171
------------------
7272

73-
We demonstrate how these concepts come into play using a simple example from PPP - FortyTwo. The original script in haskell can be found here `here <https://github.com/input-output-hk/plutus-pioneer-program/blob/28559d379df8b66c06d8fbd1e2a43f6a8351382a/code/week02/src/Week02/Typed.hs>`_. Using PyCardano, we will show one can send and lock funds at a script address, and how someone else with the correct redeemer value can unlock and receive the funds.
73+
We demonstrate how these concepts come into play using a simple example from `eopsin <https://github.com/ImperatorLang/eopsin>`_.
74+
A user can lock funds together with a public key hash.
75+
The contract will make sure that only the owner of the matching private key can redeem the gift.
76+
77+
We will first compile the contract locally. For this, you will need to have installed python3.8.
7478

7579
Step 1
7680

81+
Open a file called ``gift.py`` and fill it with the following code:::
82+
83+
from eopsin.prelude import *
84+
85+
@dataclass()
86+
class CancelDatum(PlutusData):
87+
pubkeyhash: bytes
88+
89+
90+
def validator(datum: CancelDatum, redeemer: None, context: ScriptContext) -> None:
91+
sig_present = False
92+
for s in context.tx_info.signatories:
93+
if datum.pubkeyhash == s:
94+
sig_present = True
95+
assert sig_present
96+
97+
98+
Step 2
99+
100+
Install the python packages ``eopsin-lang`` and ``pyaiken``. We can then build the contract.
101+
102+
.. code:: bash
103+
104+
$ python3.8 -m venv venv
105+
$ source venv/bin/activate
106+
$ pip install eopsin-lang
107+
$ eopsin build gift.py
108+
109+
This is it! You will now find all relevant artifacts for proceeding in the folder ``gift/``.
110+
111+
Step 3
112+
113+
Back into the python console.
77114
Similar to `Transaction guide <../guides/transaction.html>`_, we build a chain context using `BlockFrostChainContext <../api/pycardano.backend.base.html#pycardano.backend.blockfrost.BlockFrostChainContext>`_::
78115

79116
>>> from pycardano import BlockFrostChainContext, Network
80-
>>> network = Network.TESTNET
117+
>>> network = Network.PREPROD
81118
>>> context = BlockFrostChainContext("your_blockfrost_project_id", network)
82119

83120
Step 2
@@ -94,53 +131,60 @@ Create script address::
94131
... TransactionBuilder,
95132
... PlutusData,
96133
... Redeemer,
134+
... PlutusV2Script,
97135
... )
98136

99-
>>> # Assuming the hexadecimal file of the script exists at your local path
100-
>>> with open("path/to/fortytwo.plutus", "r") as f:
137+
>>> # This artifact was generated in step 2
138+
>>> with open("gift/script.cbor", "r") as f:
101139
>>> script_hex = f.read()
102-
>>> forty_two_script = cbor2.loads(bytes.fromhex(script_hex))
140+
>>> gift_script = PlutusV2Script(bytes.fromhex(script_hex))
103141

104-
>>> script_hash = plutus_script_hash(forty_two_script)
142+
>>> script_hash = plutus_script_hash(gift_script)
105143
>>> script_address = Address(script_hash, network=network)
106144

107145
Step 3
108146

109-
Giver/Locker sends funds to script address::
147+
Giver/Locker sends funds to script address.
148+
We will attach the public key hash of a receiver address as datum to the utxo.
149+
Note that we will just use the datatype defined in the contract, as it also uses ``PlutusData``.
150+
151+
::
110152

111153
>>> payment_vkey = PaymentVerificationKey.load("path/to/payment.vkey")
112154
>>> payment_skey = PaymentSigningKey.load("path/to/payment.skey")
113155
>>> giver_address = Address(payment_vkey.hash(), network=network)
114156

157+
>>> payment_vkey_2 = PaymentVerificationKey.load("path/to/payment2.vkey")
158+
>>> payment_skey_2 = PaymentSigningKey.load("path/to/payment2.skey")
159+
>>> taker_address = Address(payment_vkey_2.hash(), network=network)
160+
115161
>>> builder = TransactionBuilder(context)
116162
>>> builder.add_input_address(giver_address)
117163

118-
>>> datum = PlutusData() # A Unit type "()" in Haskell
164+
>>> from gift import CancelDatum
165+
>>> datum = CancelDatum(payment_vkey_2.hash().to_primitive())
119166
>>> builder.add_output(
120167
>>> TransactionOutput(script_address, 50000000, datum_hash=datum_hash(datum))
121168
>>> )
122169

123170
Build, sign and submit the transaction:
124171

125-
>>> signed_tx = builder.build_and_sign([payment_skey], giver_address)
126-
>>> context.submit_tx(signed_tx.to_cbor())
172+
>>> signed_tx = builder.build_and_sign([payment_skey], giver_address)
173+
>>> context.submit_tx(signed_tx.to_cbor())
127174

128175
Step 4
129176

130-
Taker/Unlocker sends transaction to consume funds. Here we specify the redeemer tag as spend and pass in the redeemer value of 42. If the redeemer value is anything else, the validator will fail and funds won't be retrieved::
177+
Taker/Unlocker sends transaction to consume funds. Here we specify the redeemer tag as spend and pass in no special redeemer, as it is being ignored by the contract.::
131178

132-
>>> redeemer = Redeemer(RedeemerTag.SPEND, 42)
179+
>>> redeemer = Redeemer(RedeemerTag.SPEND, PlutusData()) # The plutus equivalent of None
133180

134181
>>> utxo_to_spend = context.utxos(str(script_address))[0]
135-
>>> extended_payment_vkey = PaymentVerificationKey.load("path/to/extended_payment.vkey")
136-
>>> extended_payment_skey = PaymentSigningKey.load("path/to/extended_payment.skey")
137-
>>> taker_address = Address(extended_payment_vkey.hash(), network=network)
138182

139183
>>> builder = TransactionBuilder(context)
140184

141185
Add info on the UTxO to spend, Plutus script, actual datum and the redeemer. Specify funds amount to take::
142186

143-
>>> builder.add_script_input(utxo_to_spend, forty_two_script, datum, redeemer)
187+
>>> builder.add_script_input(utxo_to_spend, gift_script, datum, redeemer)
144188
>>> take_output = TransactionOutput(taker_address, 25123456)
145189
>>> builder.add_output(take_output)
146190

@@ -157,7 +201,17 @@ Taker/Unlocker provides collateral. Collateral has been introduced in Alonzo tra
157201

158202
>>> signed_tx = builder.build_and_sign([self.extended_payment_skey], taker_address)
159203

160-
>>> chain_context.submit_tx(signed_tx.to_cbor())
204+
205+
Uh oh! That failed. We forgot to add the taker as a `required` signer, so that the contract knows
206+
that they will sign the transaction::
207+
208+
>>> builder.required_signers = [payment_vkey_2.hash()]
209+
210+
Now lets try to resubmit this::
211+
212+
>>> signed_tx = builder.build_and_sign([self.extended_payment_skey], taker_address)
213+
214+
>>> context.submit_tx(signed_tx.to_cbor())
161215

162216
The funds locked in script address is successfully retrieved to the taker address.
163217

@@ -183,7 +237,7 @@ Using the same FortyTwo example, now in Vasil, we show how reference scripts can
183237
>>> datum = 42
184238
>>> # Include scripts in the script address
185239
>>> builder.add_output(
186-
>>> TransactionOutput(script_address, 50000000, script=forty_two_script)
240+
>>> TransactionOutput(script_address, 50000000, script=gift_script)
187241
>>> )
188242

189243
With reference script, actual script doesn't need to be included in the transaction anymore in order to spend UTxO sitting at script address::
@@ -210,7 +264,7 @@ Again, with the same example, we show that you can send funds to script address
210264
>>> builder.add_input_address(giver_address)
211265
>>> datum = 42
212266
>>> builder.add_output(
213-
>>> TransactionOutput(script_address, 50000000, datum=datum, script=forty_two_script)
267+
>>> TransactionOutput(script_address, 50000000, datum=datum, script=gift_script)
214268
>>> )
215269

216270
With inline datum, we no longer have to include a datum within our transaction for our plutus spending scripts. Instead we can specify the transaction output where our datum exists to be used in conjunction with our Plutus spending script. This reduces the overall size of our transaction::

0 commit comments

Comments
 (0)