Skip to content

Commit

Permalink
https://github.com/neo-project/neo/pull/2024
Browse files Browse the repository at this point in the history
  • Loading branch information
ixje committed Jan 25, 2021
1 parent 4278e35 commit ccea76d
Show file tree
Hide file tree
Showing 8 changed files with 47 additions and 36 deletions.
5 changes: 2 additions & 3 deletions neo3/contracts/applicationengine.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,9 +341,8 @@ def call_from_native(self,
state = self._get_invocation_state(self.current_context)
state.return_type = type(None)
state.callback = on_complete
contract_call_descriptor = self._interop_calls.get(
contracts.syscall_name_to_int("contract_call_internal_ex"),
None
contract_call_descriptor = interop.InteropService.get_descriptor(
contracts.syscall_name_to_int("contract_call_internal")
)
if contract_call_descriptor is None:
raise ValueError
Expand Down
2 changes: 1 addition & 1 deletion neo3/contracts/interop/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ def contract_destroy(engine: contracts.ApplicationEngine) -> None:
engine.snapshot.storages.delete(key)


@register("contract_call_internal", 0, contracts.native.CallFlags.ALL, False, [])
def contract_call_internal(engine: contracts.ApplicationEngine,
contract_hash: types.UInt160,
method: str,
Expand All @@ -133,7 +134,6 @@ def contract_call_internal(engine: contracts.ApplicationEngine,
contract_call_internal_ex(engine, target_contract, method_descriptor, args, flags, convention)


@register("contract_call_internal_ex", 0, contracts.native.CallFlags.ALL, False, [])
def contract_call_internal_ex(engine: contracts.ApplicationEngine,
contract: storage.ContractState,
contract_method_descriptor: contracts.ContractMethodDescriptor,
Expand Down
2 changes: 1 addition & 1 deletion neo3/contracts/interop/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def do_notify(engine: contracts.ApplicationEngine, event_name: bytes, state: vm.
engine:
event_name:
state: values belonging to the notification event.
e.g. a NEP-5 transfer event might have as state: from script_hash, to script_hash and an ammount
e.g. a NEP-17 transfer event might have as state: from script_hash, to script_hash and an ammount
"""
if len(event_name) > engine.MAX_EVENT_SIZE:
raise ValueError(
Expand Down
9 changes: 8 additions & 1 deletion neo3/contracts/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ def __init__(self, contract_hash: types.UInt160 = types.UInt160.zero()):
Args:
contract_hash: the contract script hash to create a manifest for.
"""
#: Contract name
self.name: str = ""
#: A group represents a set of mutually trusted contracts. A contract will trust and allow any contract in the
#: same group to invoke it.
self.groups: List[ContractGroup] = []
Expand Down Expand Up @@ -291,7 +293,8 @@ def __len__(self):
def __eq__(self, other):
if not isinstance(other, type(self)):
return False
return (self.groups == other.groups
return (self.name == other.name
and self.groups == other.groups
and self.abi == other.abi
and self.permissions == other.permissions
and self.trusts == other.trusts
Expand Down Expand Up @@ -320,6 +323,9 @@ def deserialize(self, reader: BinaryReader) -> None:
self._deserialize_from_json(json.loads(reader.read_var_string(self.MAX_LENGTH)))

def _deserialize_from_json(self, json: dict) -> None:
self.name = json['name']
if self.name is None:
self.name = ""
self.abi = contracts.ContractABI.from_json(json['abi'])
self.contract_hash = self.abi.contract_hash
self.groups = list(map(lambda g: ContractGroup.from_json(g), json['groups']))
Expand All @@ -340,6 +346,7 @@ def to_json(self) -> dict:
"""
trusts = list(map(lambda m: "0x" + m, self.trusts.to_json()['wildcard']))
json = {
"name": self.name if self.name else None,
"groups": list(map(lambda g: g.to_json(), self.groups)),
"supportedstandards": self.supported_standards,
"abi": self.abi.to_json(),
Expand Down
43 changes: 27 additions & 16 deletions neo3/contracts/native/nativecontract.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from __future__ import annotations
from typing import List, Callable, Dict, Tuple, Iterator, Any, cast, NamedTuple
from typing import List, Callable, Dict, Tuple, Iterator, Any, cast, Optional
from neo3 import contracts, vm, storage, settings
from neo3.core import types, to_script_hash, serialization, cryptography, msgrouter, Size as s
from neo3.network import message, convenience
Expand Down Expand Up @@ -64,6 +64,7 @@ def init(self):
self._script: bytes = sb.to_array()
self._script_hash: types.UInt160 = to_script_hash(self._script)
self._manifest: contracts.ContractManifest = contracts.ContractManifest(contract_hash=self._script_hash)
self._manifest.name = self._service_name
self._manifest.abi.methods = []
self._manifest.safe_methods = contracts.WildcardContainer()
self._register_contract_method(self.supported_standards,
Expand Down Expand Up @@ -567,9 +568,9 @@ def _unblock_account(self, engine: contracts.ApplicationEngine, account: types.U
return True


class Nep5Token(NativeContract):
class Nep17Token(NativeContract):
_id: int = -99999
_service_name: str = "Nep5Token"
_service_name: str = "Nep17Token"
_decimals: int = -1

_PREFIX_ACCOUNT = b'\x14'
Expand All @@ -579,8 +580,8 @@ class Nep5Token(NativeContract):
_symbol: str = ""

def init(self):
super(Nep5Token, self).init()
self.manifest.supported_standards = ["NEP-5"]
super(Nep17Token, self).init()
self.manifest.supported_standards = ["NEP-17"]
self.manifest.abi.events = [
contracts.ContractEventDescriptor(
"Transfer",
Expand Down Expand Up @@ -658,7 +659,7 @@ def mint(self, engine: contracts.ApplicationEngine, account: types.UInt160, amou
else:
old_value = vm.BigInteger(storage_item.value)
storage_item.value = (amount + old_value).to_array()
self._notify_transfer(engine, types.UInt160.zero(), account, amount)
self._post_transfer(engine, types.UInt160.zero(), account, amount)

def burn(self, engine: contracts.ApplicationEngine, account: types.UInt160, amount: vm.BigInteger) -> None:
"""
Expand Down Expand Up @@ -698,7 +699,7 @@ def burn(self, engine: contracts.ApplicationEngine, account: types.UInt160, amou
engine.snapshot.storages.delete(storage_key)
else:
storage_item.value = new_value.to_array()
self._notify_transfer(engine, account, types.UInt160.zero(), amount)
self._post_transfer(engine, account, types.UInt160.zero(), amount)

def total_supply(self, snapshot: storage.Snapshot) -> vm.BigInteger:
""" Get the total deployed tokens. """
Expand Down Expand Up @@ -730,11 +731,11 @@ def balance_of(self, snapshot: storage.Snapshot, account: types.UInt160) -> vm.B
state = self._state.deserialize_from_bytes(storage_item.value)
return state.balance

def _notify_transfer(self,
engine: contracts.ApplicationEngine,
account_from: types.UInt160,
account_to: types.UInt160,
amount: vm.BigInteger) -> None:
def _post_transfer(self,
engine: contracts.ApplicationEngine,
account_from: types.UInt160,
account_to: types.UInt160,
amount: vm.BigInteger) -> None:
state = vm.ArrayStackItem(engine.reference_counter)
if account_from == types.UInt160.zero():
state.append(vm.NullStackItem())
Expand All @@ -748,6 +749,16 @@ def _notify_transfer(self,

msgrouter.interop_notify(self.script_hash, "Transfer", state)

# wallet or smart contract
if account_to == types.UInt160.zero() or engine.snapshot.contracts.try_get(account_to) is None:
return

engine.call_from_native(None,
account_to,
"onPayment",
[vm.ByteStringStackItem(account_from.to_array()), vm.IntegerStackItem(amount)]
)

def transfer(self,
engine: contracts.ApplicationEngine,
account_from: types.UInt160,
Expand Down Expand Up @@ -805,7 +816,7 @@ def transfer(self,
state_to.balance += amount
storage_item_to.value = state_to.to_array()
engine.snapshot.storages.update(storage_key_to, storage_item_to)
self._notify_transfer(engine, account_from, account_to, amount)
self._post_transfer(engine, account_from, account_to, amount)
return True

def on_balance_changing(self, engine: contracts.ApplicationEngine,
Expand All @@ -816,7 +827,7 @@ def on_balance_changing(self, engine: contracts.ApplicationEngine,

def supported_standards(self) -> List[str]:
""" The list of supported Neo Enhancement Proposals (NEP)."""
return ["NEP-5"]
return ["NEP-17"]


class _NeoTokenStorageState(storage.Nep5StorageState):
Expand Down Expand Up @@ -1060,7 +1071,7 @@ def deserialize(self, reader: serialization.BinaryReader) -> None:
self._records = reader.read_serializable_list(_GasRecord)


class NeoToken(Nep5Token):
class NeoToken(Nep17Token):
_id: int = -1
_service_name = "NEO"
_decimals: int = 0
Expand Down Expand Up @@ -1561,7 +1572,7 @@ def _distribute_gas(self,
GasToken().mint(engine, account, gas)


class GasToken(Nep5Token):
class GasToken(Nep17Token):
_id: int = -2
_service_name: str = "GAS"
_decimals: int = 8
Expand Down
3 changes: 3 additions & 0 deletions tests/contracts/interop/test_contract_interop.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def main() -> str:
raw_hello_world_nef = b'NEF3neo3-boa by COZ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x0c\x0bhello world@\xe3\xb9\x00\x17'

raw_hello_world_manifest = {
"name": "hello world",
"groups": [],
"abi": {
"hash": "0x20caf3711a574b0be8c5746d85db2ee1e85aed3b",
Expand All @@ -41,6 +42,7 @@ def main() -> str:

raw_bye_world_nef = b'NEF3neo3-boa by COZ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x0c\tbye world@\x9f\xff\x95\xd0'
raw_bye_world_manifest = {
"name": "bye world",
"groups": [],
"abi": {
"hash": "0xbf15664f6d3ecb0ff82ebe001257263b50a314c4",
Expand Down Expand Up @@ -80,6 +82,7 @@ def test_func2(value: int) -> int:
"""
raw_contract3_nef = b'NEF3neo3-boa by COZ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x11@\x12@W\x00\x01\x11x\x9e@P\x9c\xb5\xb0'
raw_contract3_manifest = {
"name": "contract3",
"groups": [],
"abi": {
"hash": "0xad8c3929e008a0a981dcb5e3c3a0928becdc2a41",
Expand Down
15 changes: 3 additions & 12 deletions tests/contracts/interop/test_native.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,8 @@ def tearDownClass(cls) -> None:
def test_token_standards(self):
gas_standards = contracts.GasToken().supported_standards()
neo_standards = contracts.NeoToken().supported_standards()
self.assertEqual(["NEP-5"], gas_standards)
self.assertEqual(["NEP-5"], neo_standards)
self.assertEqual(["NEP-17"], gas_standards)
self.assertEqual(["NEP-17"], neo_standards)

def test_token_symbols(self):
gas_symbol = contracts.GasToken().symbol()
Expand Down Expand Up @@ -577,8 +577,6 @@ def test_transfer_zero_amount(self):
amount = vm.BigInteger(0)

engine = self.transfer_helper(gas, account_from, account_to, amount)
# ensure the destination contract exists
engine.snapshot.contracts.put(state_to)
# ensure the source account has balance
engine.snapshot.storages.put(storage_key_from, storage_item_from)

Expand Down Expand Up @@ -614,8 +612,6 @@ def test_transfer_more_than_balance(self):
amount = account_state.balance + 1

engine = self.transfer_helper(gas, account_from, account_to, amount)
# ensure the destination contract exists
engine.snapshot.contracts.put(state_to)
# ensure the source account has balance
engine.snapshot.storages.put(storage_key_from, storage_item_from)
engine.execute()
Expand All @@ -638,8 +634,6 @@ def test_transfer_to_self(self):
amount = account_state.balance

engine = self.transfer_helper(gas, account, account, amount)
# ensure the destination contract exists
engine.snapshot.contracts.put(state_to)
# ensure the source account has balance
engine.snapshot.storages.put(storage_key_from, storage_item_from)

Expand Down Expand Up @@ -677,8 +671,7 @@ def test_transfer_full_balance(self):
amount = account_from_state.balance

engine = self.transfer_helper(gas, account_from, account_to, amount)
# ensure the destination contract exists
engine.snapshot.contracts.put(state_to)

# ensure the source account has balance
engine.snapshot.storages.put(storage_key_from, storage_item_from)

Expand Down Expand Up @@ -723,8 +716,6 @@ def test_transfer_partial_balance_to_account_with_balance(self):
amount = vm.BigInteger(50)

engine = self.transfer_helper(gas, account_from, account_to, amount)
# ensure the destination contract exists
engine.snapshot.contracts.put(state_to)
# ensure the source and destination account have balances
engine.snapshot.storages.put(storage_key_from, storage_item_from)
engine.snapshot.storages.put(storage_key_to, storage_item_to)
Expand Down
4 changes: 2 additions & 2 deletions tests/contracts/test_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,13 +227,13 @@ def setUpClass(cls) -> None:
Console.WriteLine($"{manifest.Size}");
Console.WriteLine($"{manifest.ToJson()}");
"""
cls.expected_json = {"groups":[],"supportedstandards":[],"abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":None}
cls.expected_json = {"name":None,"groups":[],"supportedstandards":[],"abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":None}

def test_create_default(self):
cm = contracts.ContractManifest(types.UInt160.zero())
self.assertEqual(self.expected_json, cm.to_json())
# see setupClass for C# reference code
self.assertEqual(212, len(cm))
self.assertEqual(224, len(cm))

def test_serialize(self):
# if test_create_default() passes, then we know `to_json()` is ok, which serialize internally uses
Expand Down

0 comments on commit ccea76d

Please sign in to comment.