Source code for genlayer.storage.root
__all__ = ('Root',)
import typing
from genlayer.storage.core import (
ROOT_SLOT_ID,
VLA,
Indirection,
Manager,
Slot,
)
from genlayer.types import Address, u8, u256
from genlayer.vm import public_abi
from ._internal.generate import InmemManager, _known_descs, generate_storage
[docs]
@generate_storage
class Root:
"""
This ABI is known and used by:
#. genvm
#. node
"""
MANAGER: typing.ClassVar[Manager] = InmemManager()
"""
Manager instance for the storage.
It is set to an actual storage manager in the runtime, but can be overridden for testing purposes.
"""
major: u8
"""
Major version of the GenVM contract expects
"""
contract_instance: Indirection[None]
code: Indirection[VLA[u8]]
"""
contract code
"""
locked_slots: Indirection[VLA[u256]]
"""
Slot ids that can not be modified after deployment. Use :py:func:`Slot.as_int` for conversion of Slot to :py:class:`int`
By default it will be populated by ``code``, ``frozen_slots``
"""
upgraders: Indirection[VLA[Address]]
code_slot: u256
"""
Slot id the contract code is stored at, as a raw 32-byte value. If zero (the
default), the code is read from the default ``code`` slot; otherwise it is read
from the pointed-to slot (see ``chain:`` runner ids).
"""
permissions: u256
"""
Permission bitfield read by the executor at execution start. Bit ``n`` corresponds
to the :py:class:`~genlayer.vm.public_abi.Permissions` member whose value is ``n``.
Use :py:meth:`get_permission` / :py:meth:`set_permission` to access it.
"""
[docs]
@staticmethod
def get() -> 'Root':
"""
Return the root storage instance.
:returns: singleton root object
"""
slot = Root.MANAGER.get_store_slot(ROOT_SLOT_ID)
return _known_descs[Root].get(slot, 0)
[docs]
def slot(self) -> Slot:
"""
Return the storage slot backing this root.
:returns: underlying storage slot
"""
return self._storage_slot # type: ignore
[docs]
def get_resolved_code(self) -> VLA[u8]:
"""
Return the storage slot where the contract code is stored.
:returns: code storage slot
"""
if self.code_slot == 0:
return self.code.get()
else:
return Root.MANAGER.get_store_slot(self.code_slot).cast(VLA[u8], 0)
[docs]
def get_vacant_slot(self) -> Slot:
"""
This slot can be used to store data without worrying about overwriting contract data.
Useful for bootstrapping contract storage
"""
return self.slot().indirect(public_abi.root_offsets.CODE_SLOT)
[docs]
def get_contract_instance[T](self, typ: typing.Type[T], /) -> T:
"""
Return the contract instance deserialized as the given type.
:param typ: storage-allowed type to deserialize into
:returns: contract instance
"""
slot: Slot = self.slot().indirect(public_abi.root_offsets.CONTRACT)
return _known_descs[typ].get(slot, 0)
[docs]
def lock_default(self):
"""
Lock the default set of slots (root, code, locked_slots, upgraders) to prevent modification after deployment.
"""
frozen = self.locked_slots.get()
frozen.append(self.slot().as_int())
frozen.append(self.code.slot().as_int())
frozen.append(self.locked_slots.slot().as_int())
frozen.append(self.upgraders.slot().as_int())
[docs]
def get_permission(self, perm: public_abi.Permissions, /) -> bool:
"""
Check whether a permission bit is set in the root ``permissions`` bitfield.
:param perm: permission to query
:returns: ``True`` iff the corresponding bit is set
"""
return (self.permissions >> perm.value) & 1 != 0
[docs]
def set_permission(self, perm: public_abi.Permissions, value: bool, /) -> None:
"""
Set or clear a permission bit in the root ``permissions`` bitfield.
:param perm: permission to modify
:param value: whether to grant the permission
"""
bit = 1 << perm.value
if value:
self.permissions |= bit
else:
self.permissions &= ~bit