Skip to content

Commit

Permalink
CU-3cr16nu - Add a new field on each type-value object on JSON manife…
Browse files Browse the repository at this point in the history
…st file generation to better describe the specific type/format of that field.
  • Loading branch information
luc10921 committed Jan 23, 2023
1 parent c954603 commit d43c0a3
Show file tree
Hide file tree
Showing 32 changed files with 758 additions and 24 deletions.
5 changes: 3 additions & 2 deletions boa3/analyser/constructanalyser.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ast

from boa3.analyser.astanalyser import IAstAnalyser
from boa3.model import set_internal_call


class ConstructAnalyser(IAstAnalyser, ast.NodeTransformer):
Expand Down Expand Up @@ -32,7 +33,7 @@ def visit_Call(self, call: ast.Call) -> ast.AST:
"""
if isinstance(call.func, ast.Attribute):
from boa3.model.builtin.builtin import Builtin
if call.func.attr == Builtin.ScriptHash.identifier:
if call.func.attr == Builtin.ScriptHashMethod_.identifier:
from boa3.constants import SYS_VERSION_INFO
from boa3.model.type.type import Type
types = {
Expand Down Expand Up @@ -66,6 +67,6 @@ def visit_Call(self, call: ast.Call) -> ast.AST:
elif isinstance(value, str):
from boa3.neo.vm.type.String import String
value = String(value).to_bytes()
return self.parse_to_node(str(to_script_hash(value)), call)
return set_internal_call(self.parse_to_node(f"UInt160({str(to_script_hash(value))})", call))

return call
49 changes: 49 additions & 0 deletions boa3/builtin/type/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,52 @@ def __rmul__(self, *args, **kwargs): # real signature unknown
Return value*self.
"""
pass


class Address(str):
"""
A class used only to indicate that a parameter or return on the manifest should be treated as an Address.
It's a subclass of str and it doesn't implement new properties or methods.
"""
pass


class BlockHash(UInt256):
"""
A class used only to indicate that a parameter or return on the manifest should be treated as a BlockHash.
It's a subclass of UInt256 and it doesn't implement new properties or methods.
"""
pass


class PublicKey(ECPoint):
"""
A class used only to indicate that a parameter or return on the manifest should be treated as a PublicKey.
It's a subclass of ECPoint and it doesn't implement new properties or methods.
"""
pass


class ScriptHash(UInt160):
"""
A class used only to indicate that a parameter or return on the manifest should be treated as a ScriptHash.
It's a subclass of UInt160 and it doesn't implement new properties or methods.
"""
pass


class ScriptHashLittleEndian(UInt160):
"""
A class used only to indicate that a parameter or return on the manifest should be treated as a
ScriptHashLittleEndian.
It's a subclass of UInt160 and it doesn't implement new properties or methods.
"""
pass


class TransactionId(UInt256):
"""
A class used only to indicate that a parameter or return on the manifest should be treated as a TransactionId.
It's a subclass of UInt256 and it doesn't implement new properties or methods.
"""
pass
93 changes: 86 additions & 7 deletions boa3/compiler/filegenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from boa3.model.method import Method
from boa3.model.symbol import ISymbol
from boa3.model.type.classes.classtype import ClassType
from boa3.model.type.itype import IType
from boa3.model.variable import Variable
from boa3.neo import to_hex_str
from boa3.neo.contracts.neffile import NefFile
Expand Down Expand Up @@ -293,20 +294,98 @@ def _construct_abi_method(self, method_id: str, method: Method) -> Dict[str, Any
abi_method_name = method.external_name if isinstance(method.external_name, str) else method_id
logging.info(f"'{abi_method_name}' method included in the manifest")

return {
method_abi = {
"name": abi_method_name,
"offset": (VMCodeMapping.instance().get_start_address(method.start_bytecode)
if method.start_bytecode is not None else 0),
"parameters": [
{
"name": arg_id,
"type": arg.type.abi_type
} for arg_id, arg in method.args.items()
self._construct_abi_type_hint(arg.type, arg_id) for arg_id, arg in method.args.items()
],
"returntype": method.type.abi_type,
"safe": method.is_safe
"safe": method.is_safe,
}

return_type_extension = self._construct_abi_type_hint(method.type, is_return_type=True)
for return_type_name, return_type in return_type_extension.items():
method_abi[return_type_name] = return_type

return method_abi

@staticmethod
def _construct_abi_type_hint(var_type: IType, var_id: Optional[str] = None, is_return_type: bool = False) -> Optional[Dict[str, Any]]:
"""
A recursive function that adds more details to some types on the manifest:
- Arrays and Maps now have new keys to indicate the type of the items ('generic', 'generickey' and 'genericitem');
- A String could have a 'hint' that it is an Address;
- A Hash160 could have a 'hint' that it is a Scripthash or ScripthashLittleEndian;
- A Hash256 could have a 'hint' that it is a BlockHash or TransactionId;
- A StorageContext could have a 'hint' that it is a StorageContext or InteropInterface;
- If the parameter or return is Optional, then the 'nullable' key will be added;
- If the parameter or return is an Union, then the 'union' key will be added, with a list of types as value.
"""
return_prefix = "return" if is_return_type else ""

extended_type = {
return_prefix + "type": var_type.abi_type
}

from boa3.model.builtin.interop.interopinterfacetype import InteropInterfaceType
from boa3.model.type.annotation.uniontype import UnionType
from boa3.model.type.collection.sequence.mutable.listtype import ListType
from boa3.model.type.collection.sequence.uint160type import UInt160Type
from boa3.model.type.collection.sequence.uint256type import UInt256Type
from boa3.model.type.collection.mapping.mutable.dicttype import DictType
from boa3.model.type.type import Type

if isinstance(var_type, InteropInterfaceType):
# Iterator or StorageContext is added as hint
extended_type[return_prefix + "hint"] = var_type.raw_identifier

# if it is a str, UInt160 or UInt256, then a type hint might be added
elif any(
isinstance(var_type, type(possible_type_hint)) and var_type.raw_identifier != possible_type_hint.raw_identifier
for possible_type_hint in [Type.str, UInt160Type.build(), UInt256Type.build()]
):
# Address, BlockHash, PublicKey, ScriptHash, ScriptHashLittleEndian or TransactionId is added as hint
extended_type[return_prefix + "hint"] = var_type.raw_identifier

# Calls itself to discover the types inside the Union/Optional
elif isinstance(var_type, UnionType):

from boa3.model.type.annotation.optionaltype import OptionalType
if isinstance(var_type, OptionalType):

# if Optional is being used only with one type, e.g., Optional[Str], then don't consider it an Union
if len(var_type.optional_types) == 1:
extended_type = FileGenerator._construct_abi_type_hint(var_type.optional_types[0],
is_return_type=is_return_type)

else:
extended_type[return_prefix + "union"] = [
FileGenerator._construct_abi_type_hint(union_type) for union_type in
var_type.optional_types
]

extended_type[return_prefix + "nullable"] = True

else:
extended_type[return_prefix + "union"] = [
FileGenerator._construct_abi_type_hint(union_type) for union_type in var_type.union_types
]

# Calls itself to discover the types inside the List
elif isinstance(var_type, ListType):
extended_type[return_prefix + "generic"] = FileGenerator._construct_abi_type_hint(var_type.item_type)

# Calls itself to discover the types inside the Dict
elif isinstance(var_type, DictType):
extended_type[return_prefix + "generickey"] = FileGenerator._construct_abi_type_hint(var_type.key_type)
extended_type[return_prefix + "genericitem"] = FileGenerator._construct_abi_type_hint(var_type.item_type)

if var_id is not None:
extended_type[return_prefix + "name"] = var_id

return extended_type

def _get_abi_events(self) -> List[Dict[str, Any]]:
"""
Gets the abi events in a dictionary format
Expand Down
22 changes: 17 additions & 5 deletions boa3/model/builtin/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from boa3.model.type.collection.sequence.uint256type import UInt256Type
from boa3.model.type.itype import IType
from boa3.model.type.math import Math
from boa3.model.type.neo.opcodetype import OpcodeType
from boa3.model.type.neo import *
from boa3.model.type.primitive.bytestringtype import ByteStringType


Expand Down Expand Up @@ -55,7 +55,7 @@ def get_by_self(cls, symbol_id: str, self_type: IType) -> Optional[Callable]:
Max = MaxIntMethod()
Min = MinIntMethod()
Print = PrintMethod()
ScriptHash = ScriptHashMethod()
ScriptHashMethod_ = ScriptHashMethod()
StrSplit = StrSplitMethod()
Sum = SumMethod()

Expand Down Expand Up @@ -145,7 +145,7 @@ def get_by_self(cls, symbol_id: str, self_type: IType) -> Optional[Callable]:
PropertyDecorator,
Range,
Reversed,
ScriptHash,
ScriptHashMethod_,
SequenceAppend,
SequenceClear,
SequenceExtend,
Expand Down Expand Up @@ -179,6 +179,12 @@ def interop_symbols(cls, package: str = None) -> Dict[str, IdentifiedSymbol]:
ECPoint = ECPointType.build()
NeoAccountState = NeoAccountStateType.build()
Opcode = OpcodeType.build()
Address = AddressType.build()
BlockHash = BlockHashType.build()
PublicKey = PublicKeyType.build()
ScriptHashType_ = ScriptHashType.build()
ScriptHashLittleEndian = ScriptHashLittleEndianType.build()
TransactionId = TransactionIdType.build()

# boa events
Nep5Transfer = Nep5TransferEvent()
Expand Down Expand Up @@ -241,14 +247,20 @@ def builtin_events(cls) -> List[EventSymbol]:
Nep11Transfer,
Nep17Transfer,
Nep5Transfer,
ScriptHash
ScriptHashMethod_
],
BoaPackage.Interop: Interop.package_symbols,
BoaPackage.Type: [ByteString,
ECPoint,
UInt160,
UInt256,
Event
Event,
Address,
BlockHash,
PublicKey,
ScriptHashType_,
ScriptHashLittleEndian,
TransactionId,
],
BoaPackage.VM: [Opcode
],
Expand Down
3 changes: 2 additions & 1 deletion boa3/model/builtin/method/toscripthashmethod.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ def __init__(self, data_type: IType = None):

identifier = 'to_script_hash'
args: Dict[str, Variable] = {'self': Variable(data_type)}
super().__init__(identifier, args, return_type=Type.bytes)
from boa3.model.type.collection.sequence.uint160type import UInt160Type
super().__init__(identifier, args, return_type=UInt160Type.build())

@property
def identifier(self) -> str:
Expand Down
2 changes: 1 addition & 1 deletion boa3/model/type/collection/sequence/ecpointtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def _init_class_symbols(self):

from boa3.model.builtin.builtin import Builtin

instance_methods = [Builtin.ScriptHash,
instance_methods = [Builtin.ScriptHashMethod_,
]

for instance_method in instance_methods:
Expand Down
17 changes: 17 additions & 0 deletions boa3/model/type/neo/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
__all__ = [
'AddressType',
'BlockHashType',
'OpcodeType',
'PublicKeyType',
'ScriptHashLittleEndianType',
'ScriptHashType',
'TransactionIdType',
]

from boa3.model.type.neo.addresstype import AddressType
from boa3.model.type.neo.blockhashtype import BlockHashType
from boa3.model.type.neo.opcodetype import OpcodeType
from boa3.model.type.neo.publickeytype import PublicKeyType
from boa3.model.type.neo.scripthashlittleendiantype import ScriptHashLittleEndianType
from boa3.model.type.neo.scripthashtype import ScriptHashType
from boa3.model.type.neo.transactionidtype import TransactionIdType
21 changes: 21 additions & 0 deletions boa3/model/type/neo/addresstype.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from typing import Any

from boa3.model.type.itype import IType
from boa3.model.type.primitive.strtype import StrType


class AddressType(StrType):
"""
A class used to indicate that a parameter or return on the manifest is an Address. It's a subclass of StrType.
"""

def __init__(self):
super().__init__()
self._identifier = 'Address'

@classmethod
def build(cls, value: Any = None) -> IType:
return _Address


_Address = AddressType()
21 changes: 21 additions & 0 deletions boa3/model/type/neo/blockhashtype.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from typing import Any

from boa3.model.type.collection.sequence.uint256type import UInt256Type
from boa3.model.type.itype import IType


class BlockHashType(UInt256Type):
"""
A class used to indicate that a parameter or return on the manifest is a BlockHash. It's a subclass of UInt256Type.
"""

def __init__(self):
super().__init__()
self._identifier = 'BlockHash'

@classmethod
def build(cls, value: Any = None) -> IType:
return _BlockHash


_BlockHash = BlockHashType()
21 changes: 21 additions & 0 deletions boa3/model/type/neo/publickeytype.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from typing import Any

from boa3.model.type.collection.sequence.ecpointtype import ECPointType
from boa3.model.type.itype import IType


class PublicKeyType(ECPointType):
"""
A class used to indicate that a parameter or return on the manifest is a PublicKey. It's a subclass of ECPointType.
"""

def __init__(self):
super().__init__()
self._identifier = 'PublicKey'

@classmethod
def build(cls, value: Any = None) -> IType:
return _PublicKey


_PublicKey = PublicKeyType()
22 changes: 22 additions & 0 deletions boa3/model/type/neo/scripthashlittleendiantype.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from typing import Any

from boa3.model.type.collection.sequence.uint160type import UInt160Type
from boa3.model.type.itype import IType


class ScriptHashLittleEndianType(UInt160Type):
"""
A class used to indicate that a parameter or return on the manifest is a ScripthashLittleEndian.
It's a subclass of UInt160Type.
"""

def __init__(self):
super().__init__()
self._identifier = 'ScriptHashLittleEndian'

@classmethod
def build(cls, value: Any = None) -> IType:
return _ScriptHashLittleEndian


_ScriptHashLittleEndian = ScriptHashLittleEndianType()
Loading

0 comments on commit d43c0a3

Please sign in to comment.