Module opshin.bridge

Bridging tools between uplc and opshin

Expand source code
"""Bridging tools between uplc and opshin"""

from functools import wraps

import re

import uplc.ast
from pycardano import PlutusData, RawCBOR

from opshin.std.bls12_381 import (
    BLS12381G1Element,
    BLS12381G2Element,
    BLS12381MillerLoopResult,
)


def to_uplc_builtin(a):
    if isinstance(a, bool):
        return uplc.ast.BuiltinBool(a)
    if isinstance(a, int):
        return uplc.ast.BuiltinInteger(a)
    if isinstance(a, str):
        return uplc.ast.BuiltinString(a)
    if isinstance(a, bytes):
        return uplc.ast.BuiltinByteString(a)
    if isinstance(a, list):
        return uplc.ast.BuiltinList(list(map(to_uplc_builtin, a)))
    if isinstance(a, PlutusData):
        return uplc.ast.data_from_cbor(a.to_cbor())
    if isinstance(a, BLS12381G1Element):
        return uplc.ast.BuiltinBLS12381G1Element(a._value)
    if isinstance(a, BLS12381G2Element):
        return uplc.ast.BuiltinBLS12381G2Element(a._value)
    if isinstance(a, BLS12381MillerLoopResult):
        return uplc.ast.BuiltinBLS12381Mlresult(a._value)
    raise NotImplementedError(f"Cannot convert {a} to uplc builtin")


def to_python(a):
    if (
        isinstance(a, uplc.ast.BuiltinInteger)
        or isinstance(a, uplc.ast.BuiltinString)
        or isinstance(a, uplc.ast.BuiltinByteString)
        or isinstance(a, uplc.ast.BuiltinBool)
    ):
        return a.value
    if isinstance(a, uplc.ast.BuiltinUnit):
        return None
    # TODO how to remap dict? use type annotations?
    if isinstance(a, uplc.ast.BuiltinList):
        return list(map(to_python, a.values))
    # TODO how to remap data? use type annotations?
    if isinstance(a, uplc.ast.PlutusData):
        return RawCBOR(uplc.ast.plutus_cbor_dumps(a))
    if isinstance(a, uplc.ast.BuiltinBLS12381G1Element):
        return BLS12381G1Element(a.value)
    if isinstance(a, uplc.ast.BuiltinBLS12381G2Element):
        return BLS12381G2Element(a.value)
    if isinstance(a, uplc.ast.BuiltinBLS12381Mlresult):
        return BLS12381MillerLoopResult(a.value)
    raise NotImplementedError(f"Cannot convert {a} to python")


def to_uplc_fun_name(snake_case_fun_name: str) -> str:
    fun_parts = []
    orig_name = snake_case_fun_name
    for match in re.finditer("[^_]+", orig_name):
        # keep underscore if starts/ends with decimal
        sub_part = match.group(0)
        if re.match(r"\d", sub_part[0]) and fun_parts and fun_parts[-1] != "_":
            fun_parts.append("_")
        fun_parts.append(sub_part.capitalize())
        if re.match(r"\d", sub_part[-1]):
            fun_parts.append("_")
    if fun_parts and fun_parts[-1] == "_":
        fun_parts.pop(-1)

    CamelCaseFunName = "".join(fun_parts)
    if CamelCaseFunName.startswith("Verify") and CamelCaseFunName.endswith(
        "_Signature"
    ):
        CamelCaseFunName = CamelCaseFunName.removesuffix("_Signature") + "Signature"
    return CamelCaseFunName


def wraps_builtin(func):
    snake_case_fun_name = func.__name__
    CamelCaseFunName = to_uplc_fun_name(snake_case_fun_name)

    @wraps(func)
    def wrapped(*args):
        """
        A UPLC builtin that was wrapped to be available in OpShin/Python.
        The type annotation of the original function is preserved.
        """
        uplc_fun = uplc.ast.BuiltInFun.__dict__[CamelCaseFunName]
        return to_python(
            uplc.ast.BuiltInFunEvalMap[uplc_fun](*(map(to_uplc_builtin, args)))
        )

    return wrapped

Functions

def to_python(a)
Expand source code
def to_python(a):
    if (
        isinstance(a, uplc.ast.BuiltinInteger)
        or isinstance(a, uplc.ast.BuiltinString)
        or isinstance(a, uplc.ast.BuiltinByteString)
        or isinstance(a, uplc.ast.BuiltinBool)
    ):
        return a.value
    if isinstance(a, uplc.ast.BuiltinUnit):
        return None
    # TODO how to remap dict? use type annotations?
    if isinstance(a, uplc.ast.BuiltinList):
        return list(map(to_python, a.values))
    # TODO how to remap data? use type annotations?
    if isinstance(a, uplc.ast.PlutusData):
        return RawCBOR(uplc.ast.plutus_cbor_dumps(a))
    if isinstance(a, uplc.ast.BuiltinBLS12381G1Element):
        return BLS12381G1Element(a.value)
    if isinstance(a, uplc.ast.BuiltinBLS12381G2Element):
        return BLS12381G2Element(a.value)
    if isinstance(a, uplc.ast.BuiltinBLS12381Mlresult):
        return BLS12381MillerLoopResult(a.value)
    raise NotImplementedError(f"Cannot convert {a} to python")
def to_uplc_builtin(a)
Expand source code
def to_uplc_builtin(a):
    if isinstance(a, bool):
        return uplc.ast.BuiltinBool(a)
    if isinstance(a, int):
        return uplc.ast.BuiltinInteger(a)
    if isinstance(a, str):
        return uplc.ast.BuiltinString(a)
    if isinstance(a, bytes):
        return uplc.ast.BuiltinByteString(a)
    if isinstance(a, list):
        return uplc.ast.BuiltinList(list(map(to_uplc_builtin, a)))
    if isinstance(a, PlutusData):
        return uplc.ast.data_from_cbor(a.to_cbor())
    if isinstance(a, BLS12381G1Element):
        return uplc.ast.BuiltinBLS12381G1Element(a._value)
    if isinstance(a, BLS12381G2Element):
        return uplc.ast.BuiltinBLS12381G2Element(a._value)
    if isinstance(a, BLS12381MillerLoopResult):
        return uplc.ast.BuiltinBLS12381Mlresult(a._value)
    raise NotImplementedError(f"Cannot convert {a} to uplc builtin")
def to_uplc_fun_name(snake_case_fun_name: str) ‑> str
Expand source code
def to_uplc_fun_name(snake_case_fun_name: str) -> str:
    fun_parts = []
    orig_name = snake_case_fun_name
    for match in re.finditer("[^_]+", orig_name):
        # keep underscore if starts/ends with decimal
        sub_part = match.group(0)
        if re.match(r"\d", sub_part[0]) and fun_parts and fun_parts[-1] != "_":
            fun_parts.append("_")
        fun_parts.append(sub_part.capitalize())
        if re.match(r"\d", sub_part[-1]):
            fun_parts.append("_")
    if fun_parts and fun_parts[-1] == "_":
        fun_parts.pop(-1)

    CamelCaseFunName = "".join(fun_parts)
    if CamelCaseFunName.startswith("Verify") and CamelCaseFunName.endswith(
        "_Signature"
    ):
        CamelCaseFunName = CamelCaseFunName.removesuffix("_Signature") + "Signature"
    return CamelCaseFunName
def wraps_builtin(func)
Expand source code
def wraps_builtin(func):
    snake_case_fun_name = func.__name__
    CamelCaseFunName = to_uplc_fun_name(snake_case_fun_name)

    @wraps(func)
    def wrapped(*args):
        """
        A UPLC builtin that was wrapped to be available in OpShin/Python.
        The type annotation of the original function is preserved.
        """
        uplc_fun = uplc.ast.BuiltInFun.__dict__[CamelCaseFunName]
        return to_python(
            uplc.ast.BuiltInFunEvalMap[uplc_fun](*(map(to_uplc_builtin, args)))
        )

    return wrapped