Source code for cometa.scripts.script

"""
Copyright 2025 Biglup Labs.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Union, Any

from .._ffi import ffi, lib
from ..errors import CardanoError
from ..cbor.cbor_reader import CborReader
from ..cbor.cbor_writer import CborWriter
from .script_language import ScriptLanguage

if TYPE_CHECKING:
    from .native_scripts.native_script import NativeScript
    from .native_scripts.script_pubkey import ScriptPubkey
    from .native_scripts.script_all import ScriptAll
    from .native_scripts.script_any import ScriptAny
    from .native_scripts.script_n_of_k import ScriptNOfK
    from .native_scripts.script_invalid_before import ScriptInvalidBefore
    from .native_scripts.script_invalid_after import ScriptInvalidAfter
    from .plutus_scripts.plutus_v1_script import PlutusV1Script
    from .plutus_scripts.plutus_v2_script import PlutusV2Script
    from .plutus_scripts.plutus_v3_script import PlutusV3Script

    NativeScriptLike = Union[
        NativeScript,
        ScriptPubkey,
        ScriptAll,
        ScriptAny,
        ScriptNOfK,
        ScriptInvalidBefore,
        ScriptInvalidAfter,
    ]

    ScriptLike = Union[
        NativeScript,
        ScriptPubkey,
        ScriptAll,
        ScriptAny,
        ScriptNOfK,
        ScriptInvalidBefore,
        ScriptInvalidAfter,
        PlutusV1Script,
        PlutusV2Script,
        PlutusV3Script,
    ]

    PlutusScriptLike = Union[
        PlutusV1Script,
        PlutusV2Script,
        PlutusV3Script,
    ]
else:
    # At runtime, we use Any to avoid circular imports
    NativeScriptLike = Any # pylint: disable=invalid-name
    ScriptLike = Any  # pylint: disable=invalid-name
    PlutusScriptLike = Any  # pylint: disable=invalid-name

[docs] class Script: """ Represents a script in Cardano. A script is a program that decides whether the transaction that spends the output is authorized to do so. Scripts can be native scripts or Plutus scripts (V1, V2, or V3). """
[docs] def __init__(self, ptr) -> None: if ptr == ffi.NULL: raise CardanoError("Script: invalid handle") self._ptr = ptr
def __del__(self) -> None: if getattr(self, "_ptr", ffi.NULL) not in (None, ffi.NULL): ptr_ptr = ffi.new("cardano_script_t**", self._ptr) lib.cardano_script_unref(ptr_ptr) self._ptr = ffi.NULL
[docs] def __enter__(self) -> Script: return self
[docs] def __exit__(self, exc_type, exc_val, exc_tb) -> None: pass
[docs] def __repr__(self) -> str: return f"Script(language={self.language.name}, hash={self.hash.hex()})"
[docs] @classmethod def from_native(cls, native_script: NativeScriptLike) -> Script: """ Creates a Script from a NativeScript. Args: native_script: The NativeScript to wrap. Returns: A new Script instance. Raises: CardanoError: If creation fails. """ from .native_scripts.native_script import NativeScript from .native_scripts.script_pubkey import ScriptPubkey from .native_scripts.script_all import ScriptAll from .native_scripts.script_any import ScriptAny from .native_scripts.script_n_of_k import ScriptNOfK from .native_scripts.script_invalid_before import ScriptInvalidBefore from .native_scripts.script_invalid_after import ScriptInvalidAfter # Convert specific script types to NativeScript if isinstance(native_script, ScriptPubkey): native_script = NativeScript.from_pubkey(native_script) elif isinstance(native_script, ScriptAll): native_script = NativeScript.from_all(native_script) elif isinstance(native_script, ScriptAny): native_script = NativeScript.from_any(native_script) elif isinstance(native_script, ScriptNOfK): native_script = NativeScript.from_n_of_k(native_script) elif isinstance(native_script, ScriptInvalidBefore): native_script = NativeScript.from_invalid_before(native_script) elif isinstance(native_script, ScriptInvalidAfter): native_script = NativeScript.from_invalid_after(native_script) else: if not isinstance(native_script, NativeScript): raise TypeError( f"Expected NativeScript or native script type, got {type(native_script).__name__}" ) out = ffi.new("cardano_script_t**") err = lib.cardano_script_new_native(native_script._ptr, out) if err != 0: raise CardanoError( f"Failed to create Script from native (error code: {err})" ) return cls(out[0])
[docs] @classmethod def from_plutus_v1(cls, plutus_v1_script: PlutusV1Script) -> Script: """ Creates a Script from a PlutusV1Script. Args: plutus_v1_script: The PlutusV1Script to wrap. Returns: A new Script instance. Raises: CardanoError: If creation fails. """ out = ffi.new("cardano_script_t**") err = lib.cardano_script_new_plutus_v1(plutus_v1_script._ptr, out) if err != 0: raise CardanoError( f"Failed to create Script from Plutus V1 (error code: {err})" ) return cls(out[0])
[docs] @classmethod def from_plutus_v2(cls, plutus_v2_script: PlutusV2Script) -> Script: """ Creates a Script from a PlutusV2Script. Args: plutus_v2_script: The PlutusV2Script to wrap. Returns: A new Script instance. Raises: CardanoError: If creation fails. """ out = ffi.new("cardano_script_t**") err = lib.cardano_script_new_plutus_v2(plutus_v2_script._ptr, out) if err != 0: raise CardanoError( f"Failed to create Script from Plutus V2 (error code: {err})" ) return cls(out[0])
[docs] @classmethod def from_plutus_v3(cls, plutus_v3_script: PlutusV3Script) -> Script: """ Creates a Script from a PlutusV3Script. Args: plutus_v3_script: The PlutusV3Script to wrap. Returns: A new Script instance. Raises: CardanoError: If creation fails. """ out = ffi.new("cardano_script_t**") err = lib.cardano_script_new_plutus_v3(plutus_v3_script._ptr, out) if err != 0: raise CardanoError( f"Failed to create Script from Plutus V3 (error code: {err})" ) return cls(out[0])
[docs] @classmethod def from_cbor(cls, reader: CborReader) -> Script: """ Deserializes a Script from CBOR data. Args: reader: A CborReader positioned at the script data. Returns: A new Script deserialized from the CBOR data. Raises: CardanoError: If deserialization fails. """ out = ffi.new("cardano_script_t**") err = lib.cardano_script_from_cbor(reader._ptr, out) if err != 0: raise CardanoError( f"Failed to deserialize Script from CBOR (error code: {err})" ) return cls(out[0])
[docs] def to_cbor(self, writer: CborWriter) -> None: """ Serializes the script to CBOR format. Args: writer: A CborWriter to write the serialized data to. Raises: CardanoError: If serialization fails. """ err = lib.cardano_script_to_cbor(self._ptr, writer._ptr) if err != 0: raise CardanoError( f"Failed to serialize Script to CBOR (error code: {err})" )
@property def language(self) -> ScriptLanguage: """ The language of this script. Returns: The ScriptLanguage value. """ lang_out = ffi.new("cardano_script_language_t*") err = lib.cardano_script_get_language(self._ptr, lang_out) if err != 0: raise CardanoError(f"Failed to get language (error code: {err})") return ScriptLanguage(lang_out[0]) @property def hash(self) -> bytes: """ The hash of this script. Returns: The 28-byte Blake2b hash of the script. """ ptr = lib.cardano_script_get_hash(self._ptr) if ptr == ffi.NULL: raise CardanoError("Failed to get script hash") data_ptr = lib.cardano_blake2b_hash_get_data(ptr) size = lib.cardano_blake2b_hash_get_bytes_size(ptr) result = bytes(ffi.buffer(data_ptr, size)) hash_ptr = ffi.new("cardano_blake2b_hash_t**", ptr) lib.cardano_blake2b_hash_unref(hash_ptr) return result
[docs] def to_native(self) -> NativeScript: """ Converts this script to a NativeScript. Returns: The NativeScript if this is a native script. Raises: CardanoError: If conversion fails or type mismatch. """ from .native_scripts.native_script import NativeScript out = ffi.new("cardano_native_script_t**") err = lib.cardano_script_to_native(self._ptr, out) if err != 0: raise CardanoError(f"Failed to convert to native (error code: {err})") return NativeScript(out[0])
[docs] def to_plutus_v1(self) -> PlutusV1Script: """ Converts this script to a PlutusV1Script. Returns: The PlutusV1Script if this is a Plutus V1 script. Raises: CardanoError: If conversion fails or type mismatch. """ from .plutus_scripts.plutus_v1_script import PlutusV1Script out = ffi.new("cardano_plutus_v1_script_t**") err = lib.cardano_script_to_plutus_v1(self._ptr, out) if err != 0: raise CardanoError(f"Failed to convert to Plutus V1 (error code: {err})") return PlutusV1Script(out[0])
[docs] def to_plutus_v2(self) -> PlutusV2Script: """ Converts this script to a PlutusV2Script. Returns: The PlutusV2Script if this is a Plutus V2 script. Raises: CardanoError: If conversion fails or type mismatch. """ from .plutus_scripts.plutus_v2_script import PlutusV2Script out = ffi.new("cardano_plutus_v2_script_t**") err = lib.cardano_script_to_plutus_v2(self._ptr, out) if err != 0: raise CardanoError(f"Failed to convert to Plutus V2 (error code: {err})") return PlutusV2Script(out[0])
[docs] def to_plutus_v3(self) -> PlutusV3Script: """ Converts this script to a PlutusV3Script. Returns: The PlutusV3Script if this is a Plutus V3 script. Raises: CardanoError: If conversion fails or type mismatch. """ from .plutus_scripts.plutus_v3_script import PlutusV3Script out = ffi.new("cardano_plutus_v3_script_t**") err = lib.cardano_script_to_plutus_v3(self._ptr, out) if err != 0: raise CardanoError(f"Failed to convert to Plutus V3 (error code: {err})") return PlutusV3Script(out[0])
[docs] def __eq__(self, other: object) -> bool: """Checks equality with another Script.""" if not isinstance(other, Script): return NotImplemented return bool(lib.cardano_script_equals(self._ptr, other._ptr))