Skip to content

Commit

Permalink
https://github.com/neo-project/neo/pull/2119
Browse files Browse the repository at this point in the history
utterly broken after this but not fixing until all preview + preview 5 changes are in
  • Loading branch information
ixje committed Feb 1, 2021
1 parent 0b7ce6f commit 5d9c730
Show file tree
Hide file tree
Showing 19 changed files with 384 additions and 754 deletions.
3 changes: 2 additions & 1 deletion neo3/contracts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
GasToken,
OracleContract,
DesignateContract,
DesignateRole)
DesignateRole,
ManagementContract)
from .checkreturn import ReturnTypeConvention
from .applicationengine import ApplicationEngine

Expand Down
10 changes: 9 additions & 1 deletion neo3/contracts/applicationengine.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ def checkwitness(self, hash_: types.UInt160) -> bool:
contracts.native.CallFlags(self.current_context.call_flags):
raise ValueError("Context requires callflags ALLOW_STATES")

contract = self.snapshot.contracts.get(self.calling_scripthash)
contract = contracts.ManagementContract().get_contract(self.snapshot, self.calling_scripthash)
if contract is None:
return False
group_keys = set(map(lambda g: g.public_key, contract.manifest.groups))
if any(group_keys.intersection(signer.allowed_groups)):
return True
Expand Down Expand Up @@ -377,3 +379,9 @@ def load_contract(self,
if init is not None:
self.load_context(context.clone(init.offset), False)
return context

def call_native(self, name: str) -> None:
contract = contracts.ManagementContract().get_contract_by_name(name)
if contract is None or contract.active_block_index > self.snapshot.persisting_block.index:
raise ValueError
contract.invoke(self)
1 change: 0 additions & 1 deletion neo3/contracts/interop/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,5 @@
from .crypto import __name__
from .json import __name__
from .enumerator import IIterator, IEnumerator, StorageIterator
from .native import __name__
from .runtime import __name__
from .storage import _storage_put_internal, MAX_STORAGE_VALUE_SIZE, MAX_STORAGE_KEY_SIZE
191 changes: 23 additions & 168 deletions neo3/contracts/interop/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,172 +6,6 @@
from neo3.contracts.interop import register


@register("System.Contract.Create", 0, contracts.native.CallFlags.WRITE_STATES, False, [bytes, bytes])
def contract_create(engine: contracts.ApplicationEngine, nef_file: bytes, manifest: bytes) -> None:
if not isinstance(engine.script_container, payloads.Transaction):
raise ValueError("Cannot create contract without a Transaction script container")

nef_len = len(nef_file)
manifest_len = len(manifest)
if (nef_len == 0
or nef_len > engine.MAX_CONTRACT_LENGTH
or manifest_len == 0
or manifest_len > contracts.ContractManifest.MAX_LENGTH):
raise ValueError("Invalid NEF or manifest length")

engine.add_gas(engine.STORAGE_PRICE * (nef_len + manifest_len))

nef = contracts.NEF.deserialize_from_bytes(nef_file)
sb = vm.ScriptBuilder()
sb.emit(vm.OpCode.ABORT)
sb.emit_push(engine.script_container.sender.to_array())
sb.emit_push(nef.script)
hash_ = to_script_hash(sb.to_array())

contract = engine.snapshot.contracts.try_get(hash_)
if contract is not None:
raise ValueError("Contract already exists")

new_id = engine.snapshot.contract_id + 1
engine.snapshot.contract_id = new_id

contract = storage.ContractState(
new_id,
nef.script,
contracts.ContractManifest.from_json(json.loads(manifest.decode())),
0,
hash_
)

if not contract.manifest.is_valid(hash_):
raise ValueError("Error: invalid manifest")

engine.snapshot.contracts.put(contract)

engine.push(engine._native_to_stackitem(contract, storage.ContractState))
method_descriptor = contract.manifest.abi.get_method("_deploy")
if method_descriptor is not None:
contract_call_internal_ex(engine,
contract,
method_descriptor,
vm.ArrayStackItem(engine.reference_counter, vm.BooleanStackItem(False)),
contracts.native.CallFlags.ALL,
contracts.ReturnTypeConvention.ENSURE_IS_EMPTY
)


@register("System.Contract.Update", 0, contracts.native.CallFlags.WRITE_STATES, False, [bytes, bytes])
def contract_update(engine: contracts.ApplicationEngine, nef_file: bytes, manifest: bytes) -> None:
nef_len = len(nef_file)
manifest_len = len(manifest)

engine.add_gas(engine.STORAGE_PRICE * (nef_len + manifest_len))

contract = engine.snapshot.contracts.try_get(engine.current_scripthash, read_only=False)
if contract is None:
raise ValueError("Can't find contract to update")

if nef_len == 0:
raise ValueError(f"Invalid NEF length: {nef_len}")

nef = contracts.NEF.deserialize_from_bytes(nef_file)
# update contract
contract.script = nef.script

if manifest_len == 0 or manifest_len > contracts.ContractManifest.MAX_LENGTH:
raise ValueError(f"Invalid manifest length: {manifest_len}")

contract.manifest = contracts.ContractManifest.from_json(json.loads(manifest.decode()))
if not contract.manifest.is_valid(contract.hash_):
raise ValueError("Error: manifest does not match with script")

contract.update_counter += 1

if len(nef_file) != 0:
method_descriptor = contract.manifest.abi.get_method("_deploy")
if method_descriptor is not None:
contract_call_internal_ex(engine,
contract,
method_descriptor,
vm.ArrayStackItem(engine.reference_counter, vm.BooleanStackItem(True)),
contracts.native.CallFlags.ALL,
contracts.ReturnTypeConvention.ENSURE_IS_EMPTY
)


@register("System.Contract.Destroy", 1000000, contracts.native.CallFlags.WRITE_STATES, False)
def contract_destroy(engine: contracts.ApplicationEngine) -> None:
hash_ = engine.current_scripthash
contract = engine.snapshot.contracts.try_get(hash_)

if contract is None:
return

engine.snapshot.contracts.delete(hash_)

for key, _ in engine.snapshot.storages.find(contract.script_hash(), b''):
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,
args: vm.ArrayStackItem,
flags: contracts.native.CallFlags,
convention: contracts.ReturnTypeConvention) -> None:
if method.startswith('_'):
raise ValueError("[System.Contract.Call] Method not allowed to start with _")

target_contract = engine.snapshot.contracts.try_get(contract_hash, read_only=True)
if target_contract is None:
raise ValueError("[System.Contract.Call] Can't find target contract")

method_descriptor = target_contract.manifest.abi.get_method(method)
if method_descriptor is None:
raise ValueError(f"[System.Contract.Call] Method '{method}' does not exist on target contract")

current_contract = engine.snapshot.contracts.try_get(engine.current_scripthash, read_only=True)
if current_contract and not current_contract.can_call(target_contract, method):
raise ValueError(f"[System.Contract.Call] Not allowed to call target method '{method}' according to manifest")

contract_call_internal_ex(engine, target_contract, method_descriptor, args, flags, convention)


def contract_call_internal_ex(engine: contracts.ApplicationEngine,
contract: storage.ContractState,
contract_method_descriptor: contracts.ContractMethodDescriptor,
args: vm.ArrayStackItem,
flags: contracts.native.CallFlags,
convention: contracts.ReturnTypeConvention) -> None:
counter = engine._invocation_counter.get(contract.hash, 0)
engine._invocation_counter.update({contract.hash: counter + 1})

engine._get_invocation_state(engine.current_context).convention = convention

state = engine.current_context
calling_flags = state.call_flags

arg_len = len(args)
expected_len = len(contract_method_descriptor.parameters)
if arg_len != expected_len:
raise ValueError(
f"[System.Contract.Call] Invalid number of contract arguments. Expected {expected_len} actual {arg_len}") # noqa

context_new = engine.load_contract(contract, contract_method_descriptor.name, flags & calling_flags)
if context_new is None:
raise ValueError
context_new.calling_script = state.script

if contracts.NativeContract.is_native(contract.hash):
context_new.evaluation_stack.push(args)
context_new.evaluation_stack.push(vm.ByteStringStackItem(contract_method_descriptor.name.encode('utf-8')))
else:
for item in reversed(args):
context_new.evaluation_stack.push(item)
context_new.ip = contract_method_descriptor.offset


@register("System.Contract.Call", 1000000, contracts.native.CallFlags.ALLOW_CALL, False,
[types.UInt160, str, vm.ArrayStackItem])
def contract_call(engine: contracts.ApplicationEngine,
Expand All @@ -192,12 +26,15 @@ def contract_callex(engine: contracts.ApplicationEngine,
# and will thrown an exception while converting the arguments for the function
# if ((callFlags & ~CallFlags.All) != 0)
# throw new ArgumentOutOfRangeException(nameof(callFlags));
contract_call_internal(engine, contract_hash, method, args, flags, contracts.ReturnTypeConvention.ENSURE_NOT_EMPTY)
# TODO: fix
# contract_call_internal(engine,
# contract_hash, method, args, flags, contracts.ReturnTypeConvention.ENSURE_NOT_EMPTY)
pass


@register("System.Contract.IsStandard", 30000, contracts.native.CallFlags.READ_STATES, True, [types.UInt160])
def contract_is_standard(engine: contracts.ApplicationEngine, hash_: types.UInt160) -> bool:
contract = engine.snapshot.contracts.try_get(hash_)
contract = contracts.ManagementContract().get_contract(engine.snapshot, hash_)
if contract:
return (contracts.Contract.is_signature_contract(contract.script)
or contracts.Contract.is_multisig_contract(contract.script))
Expand All @@ -220,3 +57,21 @@ def get_callflags(engine: contracts.ApplicationEngine) -> contracts.native.CallF
def contract_create_standard_account(engine: contracts.ApplicationEngine,
public_key: cryptography.ECPoint) -> types.UInt160:
return to_script_hash(contracts.Contract.create_signature_redeemscript(public_key))


@register("System.Contract.NativeOnPersist", 0, contracts.CallFlags.WRITE_STATES, False, [])
def native_on_persist(engine: contracts.ApplicationEngine) -> None:
if engine.trigger != contracts.TriggerType.ON_PERSIST:
raise SystemError()
for contract in contracts.NativeContract._contracts.values():
if contract.active_block_index <= engine.snapshot.persisting_block.index:
contract.on_persist(engine)


@register("System.Contract.NativePostPersist", 0, contracts.CallFlags.WRITE_STATES, False, [])
def native_post_persist(engine: contracts.ApplicationEngine) -> None:
if engine.trigger != contracts.TriggerType.POST_PERSIST:
raise SystemError()
for contract in contracts.NativeContract._contracts.values():
if contract.active_block_index <= engine.snapshot.persisting_block.index:
contract.post_persist(engine)
18 changes: 0 additions & 18 deletions neo3/contracts/interop/native.py

This file was deleted.

21 changes: 13 additions & 8 deletions neo3/contracts/interop/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,32 @@

@register("System.Storage.GetContext", 400, contracts.native.CallFlags.READ_STATES, False, [])
def get_context(engine: contracts.ApplicationEngine) -> storage.StorageContext:
return storage.StorageContext(engine.current_scripthash, False)
contract = contracts.ManagementContract().get_contract(engine.snapshot, engine.current_scripthash)
if contract is None:
raise ValueError("Contract not deployed")
return storage.StorageContext(contract.id, False)


@register("System.Storage.GetReadOnlyContext", 400, contracts.native.CallFlags.READ_STATES, False, [])
def get_read_only_context(engine: contracts.ApplicationEngine) -> storage.StorageContext:
contract = engine.snapshot.contracts.try_get(engine.current_scripthash, read_only=True)
return storage.StorageContext(contract.script_hash(), True)
contract = contracts.ManagementContract().get_contract(engine.snapshot, engine.current_scripthash)
if contract is None:
raise ValueError("Contract not deployed")
return storage.StorageContext(contract.id, True)


@register("System.Storage.AsReadOnly", 400, contracts.native.CallFlags.READ_STATES, False, [storage.StorageContext])
def context_as_read_only(engine: contracts.ApplicationEngine,
context: storage.StorageContext) -> storage.StorageContext:
if not context.is_read_only:
context = storage.StorageContext(context.script_hash, True)
context = storage.StorageContext(context.id, True)
return context


@register("System.Storage.Get", 1000000, contracts.native.CallFlags.READ_STATES, False,
[storage.StorageContext, bytes])
def storage_get(engine: contracts.ApplicationEngine, context: storage.StorageContext, key: bytes) -> Optional[bytes]:
storage_key = storage.StorageKey(context.script_hash, key)
storage_key = storage.StorageKey(context.id, key)
item = engine.snapshot.storages.try_get(storage_key, read_only=True)
if item is not None:
return item.value
Expand All @@ -41,7 +46,7 @@ def storage_get(engine: contracts.ApplicationEngine, context: storage.StorageCon
@register("System.Storage.Find", 1000000, contracts.native.CallFlags.READ_STATES, False,
[storage.StorageContext, bytes])
def storage_find(engine: contracts.ApplicationEngine, context: storage.StorageContext, key: bytes) -> IIterator:
it = StorageIterator(engine.snapshot.storages.find(context.script_hash, key))
it = StorageIterator(engine.snapshot.storages.find(context.id, key))
return it


Expand All @@ -57,7 +62,7 @@ def _storage_put_internal(engine: contracts.ApplicationEngine,
if context.is_read_only:
raise ValueError("Cannot persist to read-only storage context")

storage_key = storage.StorageKey(context.script_hash, key)
storage_key = storage.StorageKey(context.id, key)
item = engine.snapshot.storages.try_get(storage_key, read_only=False)

is_constant = storage.StorageFlags.CONSTANT in flags
Expand Down Expand Up @@ -104,7 +109,7 @@ def storage_put_ex(engine: contracts.ApplicationEngine,
def storage_delete(engine: contracts.ApplicationEngine, context: storage.StorageContext, key: bytes) -> None:
if context.is_read_only:
raise ValueError("Cannot delete from read-only storage context")
storage_key = storage.StorageKey(context.script_hash, key)
storage_key = storage.StorageKey(context.id, key)
item = engine.snapshot.storages.try_get(storage_key)
if item and item.is_constant:
raise ValueError("Cannot delete a storage item that is marked constant")
Expand Down
4 changes: 3 additions & 1 deletion neo3/contracts/native/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from .nativecontract import (CallFlags, NativeContract, PolicyContract, NeoToken, GasToken)
from .oracle import OracleContract
from .designate import DesignateRole, DesignateContract
from .management import ManagementContract

__all__ = ['NativeContract',
'CallFlags',
'PolicyContract',
'NeoToken',
'GasToken',
'OracleContract'
'OracleContract',
'ManagementContract'
]
15 changes: 7 additions & 8 deletions neo3/contracts/native/designate.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,22 @@ class DesignateContract(NativeContract):
_id = -5

def init(self):
super(DesignateContract, self).init()
self._register_contract_method(self.get_designated_by_role,
"getDesignatedByRole",
1000000,
return_type=List[cryptography.ECPoint],
add_engine=False,
add_snapshot=True,
safe_method=True)
call_flags=contracts.native.CallFlags.READ_STATES)

self._register_contract_method(self.designate_as_role,
"designateAsRole",
0,
return_type=None,
add_engine=True,
add_snapshot=False,
safe_method=False)
call_flags=contracts.native.CallFlags.WRITE_STATES)

def get_designated_by_role(self,
snapshot: storage.Snapshot,
Expand All @@ -39,11 +40,9 @@ def get_designated_by_role(self,
if snapshot.block_height + 1 < index:
raise ValueError("[DesignateContract] Designate list index out of range")

key = storage.StorageKey(self.hash,
role.to_bytes(1, 'little') + vm.BigInteger(index).to_array()
).to_array()
boundary = storage.StorageKey(self.hash, role.to_bytes(1, 'little')) .to_array()
for _, storage_item in snapshot.storages.find_range(self.hash, key, boundary, "reverse"):
key = self.create_key(role.to_bytes(1, 'little') + vm.BigInteger(index).to_array()).to_array()
boundary = self.create_key(role.to_bytes(1, 'little')) .to_array()
for _, storage_item in snapshot.storages.find_range(key, boundary, "reverse"):
with serialization.BinaryReader(storage_item.value) as reader:
return reader.read_serializable_list(cryptography.ECPoint)
else:
Expand All @@ -67,7 +66,7 @@ def designate_as_role(self,

nodes.sort()
index = engine.snapshot.persisting_block.index + 1
storage_key = storage.StorageKey(self.hash, role.to_bytes(1, 'little') + vm.BigInteger(index).to_array())
storage_key = self.create_key(role.to_bytes(1, 'little') + vm.BigInteger(index).to_array())
with serialization.BinaryWriter() as writer:
writer.write_serializable_list(nodes)
storage_item = storage.StorageItem(writer.to_array())
Expand Down
Loading

0 comments on commit 5d9c730

Please sign in to comment.