Skip to content

generalize plutus scripts #396

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ jobs:
if: ${{ matrix.python-version == '3.11' }}
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN || '' }}
- name: Run static analyses
if: ${{ matrix.python-version == '3.11' }}
run: |
Expand Down Expand Up @@ -72,8 +72,8 @@ jobs:
if: ${{ matrix.python-version == '3.11' }}
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN || '' }}

- name: Dump docker logs
if: failure()
Expand Down
34 changes: 7 additions & 27 deletions pycardano/backend/blockfrost.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,7 @@
from pycardano.hash import SCRIPT_HASH_SIZE, DatumHash, ScriptHash
from pycardano.nativescript import NativeScript
from pycardano.network import Network
from pycardano.plutus import (
ExecutionUnits,
PlutusV1Script,
PlutusV2Script,
PlutusV3Script,
script_hash,
)
from pycardano.plutus import ExecutionUnits, PlutusScript, ScriptType, script_hash
from pycardano.serialization import RawCBOR
from pycardano.transaction import (
Asset,
Expand All @@ -42,9 +36,7 @@
__all__ = ["BlockFrostChainContext"]


def _try_fix_script(
scripth: str, script: Union[PlutusV1Script, PlutusV2Script, PlutusV3Script]
) -> Union[PlutusV1Script, PlutusV2Script, PlutusV3Script]:
def _try_fix_script(scripth: str, script: PlutusScript) -> PlutusScript:
if str(script_hash(script)) == scripth:
return script
else:
Expand Down Expand Up @@ -180,25 +172,13 @@
)
return self._protocol_param

def _get_script(
self, script_hash: str
) -> Union[PlutusV1Script, PlutusV2Script, PlutusV3Script, NativeScript]:
def _get_script(self, script_hash: str) -> ScriptType:
script_type = self.api.script(script_hash).type
if script_type == "plutusV1":
v1script = PlutusV1Script(
bytes.fromhex(self.api.script_cbor(script_hash).cbor)
)
return _try_fix_script(script_hash, v1script)
elif script_type == "plutusV2":
v2script = PlutusV2Script(
bytes.fromhex(self.api.script_cbor(script_hash).cbor)
)
return _try_fix_script(script_hash, v2script)
elif script_type == "plutusV3":
v3script = PlutusV3Script(
bytes.fromhex(self.api.script_cbor(script_hash).cbor)
if script_type.startsWith("plutusV"):
ps = PlutusScript.from_version(

Check warning on line 178 in pycardano/backend/blockfrost.py

View check run for this annotation

Codecov / codecov/patch

pycardano/backend/blockfrost.py#L178

Added line #L178 was not covered by tests
script_type[-1], bytes.fromhex(self.api.script_cbor(script_hash).cbor)
)
return _try_fix_script(script_hash, v3script)
return _try_fix_script(script_hash, ps)

Check warning on line 181 in pycardano/backend/blockfrost.py

View check run for this annotation

Codecov / codecov/patch

pycardano/backend/blockfrost.py#L181

Added line #L181 was not covered by tests
else:
script_json: JsonDict = self.api.script_json(
script_hash, return_type="json"
Expand Down
20 changes: 6 additions & 14 deletions pycardano/backend/kupo.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,7 @@
from pycardano.backend.blockfrost import _try_fix_script
from pycardano.hash import DatumHash, ScriptHash
from pycardano.network import Network
from pycardano.plutus import (
ExecutionUnits,
PlutusV1Script,
PlutusV2Script,
PlutusV3Script,
)
from pycardano.plutus import ExecutionUnits, PlutusScript
from pycardano.serialization import RawCBOR
from pycardano.transaction import (
Asset,
Expand Down Expand Up @@ -177,14 +172,11 @@ def _utxos_kupo(self, address: str) -> List[UTxO]:
if script_hash:
kupo_script_url = self._kupo_url + "/scripts/" + script_hash
script = requests.get(kupo_script_url).json()
if script["language"] == "plutus:v3":
script = PlutusV3Script(bytes.fromhex(script["script"]))
script = _try_fix_script(script_hash, script)
elif script["language"] == "plutus:v2":
script = PlutusV2Script(bytes.fromhex(script["script"]))
script = _try_fix_script(script_hash, script)
elif script["language"] == "plutus:v1":
script = PlutusV1Script(bytes.fromhex(script["script"]))
ver = int(script["language"].removeprefix("plutus:v"))
if 1 <= ver <= 3:
script = PlutusScript.from_version(
ver, bytes.fromhex(script["script"])
)
script = _try_fix_script(script_hash, script)
else:
raise ValueError("Unknown plutus script type")
Expand Down
15 changes: 6 additions & 9 deletions pycardano/backend/ogmios_v6.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@
PLUTUS_V1_COST_MODEL,
PLUTUS_V2_COST_MODEL,
ExecutionUnits,
PlutusV1Script,
PlutusV2Script,
PlutusV3Script,
PlutusScript,
)
from pycardano.serialization import RawCBOR
from pycardano.transaction import (
Expand Down Expand Up @@ -261,12 +259,11 @@ def _utxo_from_ogmios_result(self, utxo: OgmiosUtxo) -> UTxO:
script = utxo.script
if script:
# TODO: Need to test with native scripts
if script["language"] == "plutus:v3":
script = PlutusV3Script(bytes.fromhex(script["cbor"]))
elif script["language"] == "plutus:v2":
script = PlutusV2Script(bytes.fromhex(script["cbor"]))
elif script["language"] == "plutus:v1":
script = PlutusV1Script(bytes.fromhex(script["cbor"]))
if script["language"].startswith("plutus:v"):
script = PlutusScript.from_version(
int(script["language"].removeprefix("plutus:v")),
bytes.fromhex(script["cbor"]),
)
else:
raise ValueError("Unknown plutus script type")
datum_hash = (
Expand Down
72 changes: 53 additions & 19 deletions pycardano/plutus.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"Datum",
"RedeemerTag",
"ExecutionUnits",
"PlutusScript",
"PlutusV1Script",
"PlutusV2Script",
"PlutusV3Script",
Expand Down Expand Up @@ -1049,33 +1050,66 @@
Redeemers = Union[List[Redeemer], RedeemerMap]


def plutus_script_hash(
script: Union[bytes, PlutusV1Script, PlutusV2Script]
) -> ScriptHash:
def plutus_script_hash(script: Union[NativeScript, PlutusScript]) -> ScriptHash:
"""Calculates the hash of a Plutus script.

Args:
script (Union[bytes, PlutusV1Script, PlutusV2Script]): A plutus script.
script (Union[bytes, PlutusScript]): A plutus script.

Returns:
ScriptHash: blake2b hash of the script.
"""
return script_hash(script)


class PlutusV1Script(bytes):
pass
class PlutusScript(bytes):
@property
def version(self) -> int:
raise NotImplementedError("")

@classmethod
def from_version(cls, version: int, script_data: bytes) -> "PlutusScript":
if version == 1:
return PlutusV1Script(script_data)
elif version == 2:
return PlutusV2Script(script_data)
elif version == 3:
return PlutusV3Script(script_data)

Check warning on line 1077 in pycardano/plutus.py

View check run for this annotation

Codecov / codecov/patch

pycardano/plutus.py#L1077

Added line #L1077 was not covered by tests
else:
raise ValueError(f"No Plutus script class found for version {version}")

Check warning on line 1079 in pycardano/plutus.py

View check run for this annotation

Codecov / codecov/patch

pycardano/plutus.py#L1079

Added line #L1079 was not covered by tests

def get_script_hash_prefix(self) -> bytes:
raise NotImplementedError("")


class PlutusV1Script(PlutusScript):
def get_script_hash_prefix(self) -> bytes:
return bytes.fromhex("01")

@property
def version(self) -> int:
return 1


class PlutusV2Script(bytes):
pass
class PlutusV2Script(PlutusScript):
def get_script_hash_prefix(self) -> bytes:
return bytes.fromhex("02")

@property
def version(self) -> int:
return 2

class PlutusV3Script(bytes):
pass

class PlutusV3Script(PlutusScript):
def get_script_hash_prefix(self) -> bytes:
return bytes.fromhex("03")

ScriptType = Union[bytes, NativeScript, PlutusV1Script, PlutusV2Script]
@property
def version(self) -> int:
return 3


ScriptType = Union[NativeScript, PlutusScript]
"""Script type. A Union type that contains all valid script types."""


Expand All @@ -1090,17 +1124,17 @@
"""
if isinstance(script, NativeScript):
return script.hash()
elif isinstance(script, PlutusV1Script) or type(script) is bytes:
elif isinstance(script, PlutusScript):
return ScriptHash(
blake2b(bytes.fromhex("01") + script, SCRIPT_HASH_SIZE, encoder=RawEncoder)
)
elif isinstance(script, PlutusV2Script):
return ScriptHash(
blake2b(bytes.fromhex("02") + script, SCRIPT_HASH_SIZE, encoder=RawEncoder)
blake2b(
script.get_script_hash_prefix() + script,
SCRIPT_HASH_SIZE,
encoder=RawEncoder,
)
)
elif isinstance(script, PlutusV3Script):
elif type(script) is bytes:
return ScriptHash(
blake2b(bytes.fromhex("03") + script, SCRIPT_HASH_SIZE, encoder=RawEncoder)
blake2b(bytes.fromhex("01") + script, SCRIPT_HASH_SIZE, encoder=RawEncoder)
)
else:
raise TypeError(f"Unexpected script type: {type(script)}")
Expand Down
32 changes: 8 additions & 24 deletions pycardano/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,7 @@
from pycardano.metadata import AuxiliaryData
from pycardano.nativescript import NativeScript
from pycardano.network import Network
from pycardano.plutus import (
Datum,
PlutusV1Script,
PlutusV2Script,
PlutusV3Script,
RawPlutusData,
)
from pycardano.plutus import Datum, PlutusScript, RawPlutusData
from pycardano.serialization import (
ArrayCBORSerializable,
CBORSerializable,
Expand Down Expand Up @@ -310,29 +304,21 @@ def to_shallow_primitive(self):
class _Script(ArrayCBORSerializable):
_TYPE: int = field(init=False, default=0)

script: Union[NativeScript, PlutusV1Script, PlutusV2Script, PlutusV3Script]
script: Union[NativeScript, PlutusScript]

def __post_init__(self):
if isinstance(self.script, NativeScript):
self._TYPE = 0
elif isinstance(self.script, PlutusV1Script):
self._TYPE = 1
elif isinstance(self.script, PlutusV2Script):
self._TYPE = 2
else:
self._TYPE = 3
elif isinstance(self.script, PlutusScript):
self._TYPE = self.script.version

@classmethod
def from_primitive(cls: Type[_Script], values: List[Primitive]) -> _Script:
if values[0] == 0:
return cls(NativeScript.from_primitive(values[1]))
assert isinstance(values[1], bytes)
if values[0] == 1:
return cls(PlutusV1Script(values[1]))
elif values[0] == 2:
return cls(PlutusV2Script(values[1]))
else:
return cls(PlutusV3Script(values[1]))
assert isinstance(values[0], int)
return cls(PlutusScript.from_version(values[0], values[1]))


@dataclass(repr=False)
Expand Down Expand Up @@ -401,7 +387,7 @@ class _TransactionOutputPostAlonzo(MapCBORSerializable):
@property
def script(
self,
) -> Optional[Union[NativeScript, PlutusV1Script, PlutusV2Script, PlutusV3Script]]:
) -> Optional[Union[NativeScript, PlutusScript]]:
if self.script_ref:
return self.script_ref.script.script
else:
Expand All @@ -427,9 +413,7 @@ class TransactionOutput(CBORSerializable):

datum: Optional[Datum] = None

script: Optional[
Union[NativeScript, PlutusV1Script, PlutusV2Script, PlutusV3Script]
] = None
script: Optional[Union[NativeScript, PlutusScript]] = None

post_alonzo: Optional[bool] = False

Expand Down
Loading
Loading