Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CU-86drpncy1 - Add new methods on storage to get/put primitive values without the need of TypeHelper #1231

Merged
merged 1 commit into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
263 changes: 258 additions & 5 deletions boa3/builtin/interop/storage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,21 @@
'StorageContext',
'StorageMap',
'get',
'get_int',
'get_bool',
'get_str',
'get_uint160',
'get_uint256',
'get_ecpoint',
'get_context',
'get_read_only_context',
'put',
'put_int',
'put_bool',
'put_str',
'put_uint160',
'put_uint256',
'put_ecpoint',
'delete',
'find',
]
Expand All @@ -14,6 +26,7 @@
from boa3.builtin.interop.storage.findoptions import FindOptions
from boa3.builtin.interop.storage.storagecontext import StorageContext
from boa3.builtin.interop.storage.storagemap import StorageMap
from boa3.builtin.type import UInt160, UInt256, ECPoint


def get_context() -> StorageContext:
Expand All @@ -35,7 +48,7 @@ def get(key: bytes, context: StorageContext = get_context()) -> bytes:

>>> put(b'unit', 'test')
... get(b'unit')
'test'
b'test'

>>> get(b'fake_key')
''
Expand All @@ -50,6 +63,138 @@ def get(key: bytes, context: StorageContext = get_context()) -> bytes:
pass


def get_int(key: bytes, context: StorageContext = get_context()) -> int:
"""
Gets a value as integer from the persistent store based on the given key.
It's equivalent to boa3.builtin.type.helper.to_int(get(key, context))

>>> put_int(b'unit', 5)
... get_int(b'unit')
5

>>> get_int(b'fake_key')
''

:param key: value identifier in the store
:type key: bytes
:param context: storage context to be used
:type context: StorageContext
:return: the value corresponding to given key for current storage context
:rtype: int
"""
pass


def get_bool(key: bytes, context: StorageContext = get_context()) -> bool:
"""
Gets a value as boolean from the persistent store based on the given key.
It's equivalent to boa3.builtin.type.helper.to_bool(get(key, context))

>>> put_bool(b'unit', True)
... get_bool(b'unit')
True

>>> get_bool(b'fake_key')
''

:param key: value identifier in the store
:type key: bytes
:param context: storage context to be used
:type context: StorageContext
:return: the value corresponding to given key for current storage context
:rtype: bool
"""
pass


def get_str(key: bytes, context: StorageContext = get_context()) -> str:
"""
Gets a value as string from the persistent store based on the given key.
It's equivalent to boa3.builtin.type.helper.to_str(get(key, context))

>>> put_str(b'unit', 'test')
... get_str(b'unit')
'test'

>>> get_str(b'fake_key')
''

:param key: value identifier in the store
:type key: bytes
:param context: storage context to be used
:type context: StorageContext
:return: the value corresponding to given key for current storage context
:rtype: str
"""
pass


def get_uint160(key: bytes, context: StorageContext = get_context()) -> UInt160:
"""
Gets a value as UInt160 from the persistent store based on the given key.
It's equivalent UInt160(get(key, context))

>>> put_uint160(b'unit', UInt160(b'0123456789ABCDEFGHIJ'))
... get_uint160(b'unit')
UInt160(0x4a49484746454443424139383736353433323130)

>>> get_uint160(b'fake_key')
''

:param key: value identifier in the store
:type key: bytes
:param context: storage context to be used
:type context: StorageContext
:return: the value corresponding to given key for current storage context
:rtype: UInt160
"""
pass


def get_uint256(key: bytes, context: StorageContext = get_context()) -> UInt256:
"""
Gets a value as UInt256 from the persistent store based on the given key.
It's equivalent UInt256(get(key, context))

>>> put_uint256(b'unit', UInt256(b'0123456789ABCDEFGHIJKLMNOPQRSTUV'))
... get_uint256(b'unit')
UInt256(0x565554535251504f4e4d4c4b4a49484746454443424139383736353433323130)

>>> get_uint160(b'fake_key')
''

:param key: value identifier in the store
:type key: bytes
:param context: storage context to be used
:type context: StorageContext
:return: the value corresponding to given key for current storage context
:rtype: UInt256
"""
pass


def get_ecpoint(key: bytes, context: StorageContext = get_context()) -> ECPoint:
"""
Gets a value as ECPoint from the persistent store based on the given key.
It's equivalent ECPoint(get(key, context))

>>> put_ecpoint(b'unit', ECPoint(b'0123456789ABCDEFGHIJKLMNOPQRSTUVW'))
... get_ecpoint(b'unit')
ECPoint(0x57565554535251504f4e4d4c4b4a49484746454443424139383736353433323130)

>>> get_ecpoint(b'fake_key')
''

:param key: value identifier in the store
:type key: bytes
:param context: storage context to be used
:type context: StorageContext
:return: the value corresponding to given key for current storage context
:rtype: ECPoint
"""
pass


def get_read_only_context() -> StorageContext:
"""
Gets current read only storage context.
Expand All @@ -63,17 +208,125 @@ def get_read_only_context() -> StorageContext:
pass


def put(key: bytes, value: int | bytes | str, context: StorageContext = get_context()):
def put(key: bytes, value: bytes, context: StorageContext = get_context()):
"""
Inserts a given value in the key-value format into the persistent storage.
Inserts a given bytes value in the key-value format into the persistent storage.

>>> put(b'unit', 'test')
>>> put(b'unit', b'test')
None

:param key: the identifier in the store for the new value
:type key: bytes
:param value: value to be stored
:type value: bytes
:param context: storage context to be used
:type context: StorageContext
"""
pass


def put_int(key: bytes, value: int, context: StorageContext = get_context()):
"""
Inserts a given integer value in the key-value format into the persistent storage.
It's equivalent to put(key, boa3.builtin.type.helper.to_int(value), context)

>>> put_int(b'unit', 5)
None

:param key: the identifier in the store for the new value
:type key: bytes
:param value: value to be stored
:type value: int
:param context: storage context to be used
:type context: StorageContext
"""
pass


def put_bool(key: bytes, value: bool, context: StorageContext = get_context()):
"""
Inserts a given boolean value in the key-value format into the persistent storage.
It's equivalent to put(key, boa3.builtin.type.helper.to_bool(value), context)

>>> put_bool(b'unit', True)
None

:param key: the identifier in the store for the new value
:type key: bytes
:param value: value to be stored
:type value: bool
:param context: storage context to be used
:type context: StorageContext
"""
pass


def put_str(key: bytes, value: str, context: StorageContext = get_context()):
"""
Inserts a given str value in the key-value format into the persistent storage.
It's equivalent to put(key, boa3.builtin.type.helper.to_str(value), context)

>>> put_str(b'unit', 'test')
None

:param key: the identifier in the store for the new value
:type key: bytes
:param value: value to be stored
:type value: str
:param context: storage context to be used
:type context: StorageContext
"""
pass


def put_uint160(key: bytes, value: UInt160, context: StorageContext = get_context()):
"""
Inserts a given UInt160 value in the key-value format into the persistent storage.
It's equivalent to put(key, value, context) since UInt160 is a subclass of bytes

>>> put_uint160(b'unit', UInt160(b'0123456789ABCDEFGHIJ'))
None

:param key: the identifier in the store for the new value
:type key: bytes
:param value: value to be stored
:type value: UInt160
:param context: storage context to be used
:type context: StorageContext
"""
pass


def put_uint256(key: bytes, value: UInt256, context: StorageContext = get_context()):
"""
Inserts a given UInt256 value in the key-value format into the persistent storage.
It's equivalent to put(key, value, context) since UInt256 is a subclass of bytes

>>> put_uint256(b'unit', UInt256(b'0123456789ABCDEFGHIJKLMNOPQRSTUV'))
None

:param key: the identifier in the store for the new value
:type key: bytes
:param value: value to be stored
:type value: UInt256
:param context: storage context to be used
:type context: StorageContext
"""
pass


def put_ecpoint(key: bytes, value: ECPoint, context: StorageContext = get_context()):
"""
Inserts a given ECPoint value in the key-value format into the persistent storage.
It's equivalent to put(key, value, context) since ECPoint is a subclass of bytes

>>> put_ecpoint(b'unit', ECPoint(b'0123456789ABCDEFGHIJKLMNOPQRSTUVW'))
None

:param key: the identifier in the store for the new value
:type key: bytes
:param value: value to be stored
:type value: int or str or bytes
:type value: ECPoint
:param context: storage context to be used
:type context: StorageContext
"""
Expand Down
5 changes: 4 additions & 1 deletion boa3/builtin/type/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class UInt160(bytes):
"""
Represents a 160-bit unsigned integer.
"""
zero: UInt160

def __init__(self, arg: bytes | int = 0):
super().__init__()
Expand All @@ -50,6 +51,7 @@ class UInt256(bytes):
"""
Represents a 256-bit unsigned integer.
"""
zero: UInt256

def __init__(self, arg: bytes | int = 0):
super().__init__()
Expand All @@ -60,12 +62,13 @@ class ECPoint(bytes):
"""
Represents a coordinate pair for elliptic curve cryptography (ECC) structures.
"""
zero: ECPoint

def __init__(self, arg: bytes):
super().__init__()
pass

def to_script_hash(self) -> bytes:
def to_script_hash(self) -> UInt160:
"""
Converts a data to a script hash.

Expand Down
7 changes: 5 additions & 2 deletions boa3/internal/compiler/codegenerator/codegenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,11 +269,14 @@ def stack_size(self) -> int:
return len(self._stack)

def _stack_append(self, value_type: IType):
self._stack_states.append(value_type, self.last_code)
self._stack_states.append(self.last_code, value_type)

def _stack_pop(self, index: int = -1) -> IType:
return self._stack_states.pop(self.last_code, index)

def _stack_reverse(self, start: int = 0, end: int = None, *, rotate: bool = False) -> IType:
return self._stack_states.reverse(self.last_code, start, end, rotate=rotate)

@property
def last_code_start_address(self) -> int:
"""
Expand Down Expand Up @@ -2408,7 +2411,7 @@ def swap_reverse_stack_items(self, no_items: int = 0, rotate: bool = False):
if opcode is Opcode.REVERSEN and no_items > 0:
self._stack_pop()
if no_items > 0:
self._stack.reverse(-no_items, rotate=rotate)
self._stack_reverse(-no_items, rotate=rotate)

def convert_init_user_class(self, class_type: ClassType):
if isinstance(class_type, UserClass):
Expand Down
18 changes: 17 additions & 1 deletion boa3/internal/compiler/codegenerator/engine/stackmemento.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def restore_state(self, code_address):
if latest_stack is not None:
self._current_stack = latest_stack

def append(self, value: IType, code: VMCode):
def append(self, code: VMCode, value: IType):
states = self.stack_map
index = VMCodeMapping.instance().get_start_address(code)
if index in states:
Expand Down Expand Up @@ -90,6 +90,22 @@ def pop(self, code: VMCode, index: int = -1):
if len(stack) > 0:
return stack.pop(index)

def reverse(self, code: VMCode, start: int = 0, end: int = None, *, rotate: bool = False):
states = self.stack_map
stack_index = VMCodeMapping.instance().get_start_address(code)
if stack_index in states:
stack = states[stack_index]
else:
if self._current_stack is not None:
stack = self._current_stack.copy()
else:
stack = NeoStack()

self._stacks.append((code, stack))
self._current_stack = stack

return stack.reverse(start, end, rotate=rotate)


class NeoStack(IStack):
def __init__(self):
Expand Down
Loading
Loading