From 62479264c88bc45968860b31585929c5be23207c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20M=C3=BCndler?= Date: Thu, 2 Mar 2023 11:48:12 +0100 Subject: [PATCH 1/4] Add implementation of simple scripts with recursive type --- examples/smart_contracts/simple_script.py | 90 +++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 examples/smart_contracts/simple_script.py diff --git a/examples/smart_contracts/simple_script.py b/examples/smart_contracts/simple_script.py new file mode 100644 index 00000000..a0c310f6 --- /dev/null +++ b/examples/smart_contracts/simple_script.py @@ -0,0 +1,90 @@ +from eopsin.prelude import * + + +@dataclass() +class RequireSignature(PlutusData): + CONSTR_ID = 0 + vkeyhash: bytes # this is either a PubKeyHash or a VerificationKeyHash + + +@dataclass() +class RequireAllOf(PlutusData): + CONSTR_ID = 1 + scripts: List["Script"] + + +@dataclass() +class RequireAnyOf(PlutusData): + CONSTR_ID = 2 + scripts: List["Script"] + + +@dataclass() +class RequireMOf(PlutusData): + CONSTR_ID = 3 + num: int + scripts: List["Script"] + + +@dataclass() +class RequireBefore(PlutusData): + CONSTR_ID = 4 + unixtimestamp: int + + +@dataclass() +class RequireAfter(PlutusData): + CONSTR_ID = 5 + unixtimestamp: int + + +Script = Union[ + RequireSignature, + RequireMOf, + RequireAnyOf, + RequireAllOf, + RequireAfter, + RequireBefore, +] + + +def validate_script( + script: Script, signatories: List[bytes], valid_range: POSIXTimeRange +) -> bool: + if isinstance(script, RequireSignature): + res = script.vkeyhash in signatories + elif isinstance(script, RequireAllOf): + res = all( + [validate_script(s, signatories, valid_range) for s in script.scripts] + ) + elif isinstance(script, RequireAnyOf): + res = any( + [validate_script(s, signatories, valid_range) for s in script.scripts] + ) + elif isinstance(script, RequireMOf): + res = ( + sum( + [ + int(validate_script(s, signatories, valid_range)) + for s in script.scripts + ] + ) + >= script.num + ) + elif isinstance(script, RequireBefore): + res = valid_range.upper_bound < script.unixtimestamp + elif isinstance(script, RequireAfter): + res = valid_range.lower_bound > script.unixtimestamp + else: + assert False, "Invalid simple script passed" + return res + + +# to fully emulate simple script behaviour, compile with --force-three-params +# the script is a contract parameter, pass it into the build command +def validator( + script: Script, datum: None, redeemer: None, context: ScriptContext +) -> None: + assert validate_script( + script, context.tx_info.signatories, context.tx_info.valid_range + ), "Simple Script validation failed!" From aa1bd15e3392c51c2710ed63fccefce8bf859263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20M=C3=BCndler?= Date: Thu, 2 Mar 2023 12:02:45 +0100 Subject: [PATCH 2/4] Rewrite simple script without recursion --- examples/smart_contracts/simple_script.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/smart_contracts/simple_script.py b/examples/smart_contracts/simple_script.py index a0c310f6..e3d9571c 100644 --- a/examples/smart_contracts/simple_script.py +++ b/examples/smart_contracts/simple_script.py @@ -10,20 +10,20 @@ class RequireSignature(PlutusData): @dataclass() class RequireAllOf(PlutusData): CONSTR_ID = 1 - scripts: List["Script"] + scripts: List[Datum] # "Script" @dataclass() class RequireAnyOf(PlutusData): CONSTR_ID = 2 - scripts: List["Script"] + scripts: List[Datum] # "Script" @dataclass() class RequireMOf(PlutusData): CONSTR_ID = 3 num: int - scripts: List["Script"] + scripts: List[Datum] # "Script" @dataclass() @@ -49,8 +49,9 @@ class RequireAfter(PlutusData): def validate_script( - script: Script, signatories: List[bytes], valid_range: POSIXTimeRange + script_raw: Datum, signatories: List[bytes], valid_range: POSIXTimeRange ) -> bool: + script: Script = script_raw # cast to Script in the type system to avoid recursive type definition if isinstance(script, RequireSignature): res = script.vkeyhash in signatories elif isinstance(script, RequireAllOf): @@ -65,7 +66,7 @@ def validate_script( res = ( sum( [ - int(validate_script(s, signatories, valid_range)) + 1 if validate_script(s, signatories, valid_range) else 0 for s in script.scripts ] ) From 747c656a5220d360811b5542fd11adee91f28fb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20M=C3=BCndler?= Date: Thu, 2 Mar 2023 12:06:29 +0100 Subject: [PATCH 3/4] Fix construction of empty lists of type AnyType --- eopsin/typed_ast.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eopsin/typed_ast.py b/eopsin/typed_ast.py index a9293724..d119f6b6 100644 --- a/eopsin/typed_ast.py +++ b/eopsin/typed_ast.py @@ -1023,7 +1023,7 @@ def empty_list(p: Type): return plt.EmptyListList(uplc.BuiltinList([], el.sample_value)) if isinstance(p.typ, DictType): plt.EmptyDataPairList() - if isinstance(p.typ, RecordType): + if isinstance(p.typ, RecordType) or isinstance(p.typ, AnyType): return plt.EmptyDataList() raise NotImplementedError(f"Empty lists of type {p} can't be constructed yet") From 510794fef9bf68ee35b131f7c5ec7cb85b2ae69b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20M=C3=BCndler?= Date: Sun, 5 Mar 2023 18:02:50 +0100 Subject: [PATCH 4/4] Fix simple scripts for correct script context type --- examples/smart_contracts/simple_script.py | 26 +++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/examples/smart_contracts/simple_script.py b/examples/smart_contracts/simple_script.py index e3d9571c..5d90c868 100644 --- a/examples/smart_contracts/simple_script.py +++ b/examples/smart_contracts/simple_script.py @@ -73,9 +73,31 @@ def validate_script( >= script.num ) elif isinstance(script, RequireBefore): - res = valid_range.upper_bound < script.unixtimestamp + upper_bound = valid_range.upper_bound + upper_limit = upper_bound.limit + if isinstance(upper_limit, FinitePOSIXTime): + upper_closed = upper_bound.closed + if isinstance(upper_closed, TrueData): + res = upper_limit.time <= script.unixtimestamp + else: + res = upper_limit.time < script.unixtimestamp + elif isinstance(upper_limit, PosInfPOSIXTime): + res = False + elif isinstance(upper_limit, NegInfPOSIXTime): + res = True elif isinstance(script, RequireAfter): - res = valid_range.lower_bound > script.unixtimestamp + lower_bound = valid_range.lower_bound + lower_limit = lower_bound.limit + if isinstance(lower_limit, FinitePOSIXTime): + lower_closed = lower_bound.closed + if isinstance(lower_closed, TrueData): + res = lower_limit.time >= script.unixtimestamp + else: + res = lower_limit.time > script.unixtimestamp + elif isinstance(lower_limit, PosInfPOSIXTime): + res = True + elif isinstance(lower_limit, NegInfPOSIXTime): + res = False else: assert False, "Invalid simple script passed" return res