diff --git a/src/contracting/contracts/proxythis.py b/src/contracting/contracts/proxythis.py new file mode 100644 index 00000000..291a405d --- /dev/null +++ b/src/contracting/contracts/proxythis.py @@ -0,0 +1,11 @@ +@export +def proxythis(con: str): + return importlib.import_module(con).getthis() + +@export +def nestedproxythis(con: str): + return importlib.import_module(con).nested_exported() + +@export +def noproxy(): + return ctx.this, ctx.caller \ No newline at end of file diff --git a/src/contracting/contracts/thistest2.py b/src/contracting/contracts/thistest2.py new file mode 100644 index 00000000..532eb34c --- /dev/null +++ b/src/contracting/contracts/thistest2.py @@ -0,0 +1,11 @@ +@export +def exported(): + return ctx.this, ctx.caller + +@export +def getthis(): + return ctx.this, ctx.caller + +@export +def nested_exported(): + return exported() diff --git a/src/contracting/execution/runtime.py b/src/contracting/execution/runtime.py index 7758f9d1..a0018c6a 100644 --- a/src/contracting/execution/runtime.py +++ b/src/contracting/execution/runtime.py @@ -9,6 +9,7 @@ class Context: def __init__(self, base_state, maxlen=constants.RECURSION_LIMIT): self._state = [] + self._depth = [] self._base_state = base_state self._maxlen = maxlen @@ -25,13 +26,22 @@ def _get_state(self): def _add_state(self, state: dict): if self._context_changed(state['this']) and len(self._state) < self._maxlen: self._state.append(state) + self._depth.append(1) + + def _ins_state(self): + if len(self._depth) > 0: + self._depth[-1] += 1 def _pop_state(self): - if len(self._state) > 0: - self._state.pop(-1) + if len(self._state) > 0: #len(self._state) should equal len(self._depth) + self._depth[-1] -= 1 + if self._depth[-1] == 0: + self._state.pop(-1) + self._depth.pop(-1) def _reset(self): self._state = [] + self._depth = [] @property def this(self): @@ -57,7 +67,6 @@ def entry(self): def submission_name(self): return self._get_state()['submission_name'] - _context = Context({ 'this': None, 'caller': None, diff --git a/src/contracting/stdlib/bridge/access.py b/src/contracting/stdlib/bridge/access.py index 2c5eb03e..dde1f985 100644 --- a/src/contracting/stdlib/bridge/access.py +++ b/src/contracting/stdlib/bridge/access.py @@ -27,6 +27,8 @@ def __enter__(self, *args, **kwargs): if state['owner'] is not None and state['owner'] != state['caller']: raise Exception('Caller is not the owner!') + else: + rt.context._ins_state() def __exit__(self, *args, **kwargs): rt.context._pop_state() diff --git a/tests/test_state_management.py b/tests/test_state_management.py new file mode 100644 index 00000000..2130aa2f --- /dev/null +++ b/tests/test_state_management.py @@ -0,0 +1,77 @@ +import unittest +from xian.processor import TxProcessor +from contracting.client import ContractingClient +from xian.services.simulator import Simulator +from xian.constants import Constants +import os +import pathlib +class MyTestCase(unittest.TestCase): + + def setUp(self): + constants = Constants() + self.c = ContractingClient(storage_home=constants.STORAGE_HOME) + self.tx_processor = TxProcessor(client=self.c) + self.stamp_calculator = Simulator() + self.d = self.c.raw_driver + self.d.flush_full() + + # Get the directory where the script is located + script_dir = os.path.dirname(os.path.abspath(__file__)) + + # Construct absolute paths for the contract files + submission_contract_path = os.path.abspath( + os.path.join( + script_dir, + "../../contracting/src/contracting/contracts/submission.s.py", + ) + ) + + with open(submission_contract_path) as f: + contract = f.read() + self.d.set_contract(name="submission", code=contract) + + def deploy_broken_stuff(self): + # Get the directory where the script is located + script_dir = os.path.dirname(os.path.abspath(__file__)) + + proxythis_path = os.path.abspath( + os.path.join( + script_dir, + "../../contracting/src/contracting/contracts/proxythis.py", + ) + ) + with open(proxythis_path) as f: + contract = f.read() + + self.c.submit( + contract, + name="con_proxythis", + ) + + self.proxythis = self.c.get_contract("con_proxythis") + + thistest2_path = os.path.abspath( + os.path.join( + script_dir, + "../../contracting/src/contracting/contracts/thistest2.py", + ) + ) + + with open(thistest2_path) as f: + contract = f.read() + + self.c.submit( + contract, + name="con_thistest2", + ) + + self.thistest2 = self.c.get_contract("con_thistest2") + + def test_submit(self): + self.deploy_broken_stuff() + self.assertEqual(self.proxythis.proxythis(con="con_thistest2", signer="address"), ("con_thistest2", "con_proxythis")) + self.assertEqual(self.proxythis.noproxy(signer="address"), ("con_proxythis", "address")) + self.assertEqual(self.proxythis.nestedproxythis(con="con_thistest2", signer="address"), ("con_thistest2", "con_proxythis")) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file