def __init__(self, trigger: contracts.TriggerType, container: payloads.IVerifiable, snapshot: storage.Snapshot, gas: int, test_mode: bool = False ): # Do not use super() version, see # https://pybind11.readthedocs.io/en/master/advanced/classes.html#overriding-virtual-functions-in-python vm.ApplicationEngineCpp.__init__(self, test_mode) #: A ledger snapshot to use for syscalls such as "System.Blockchain.GetHeight". self.snapshot = snapshot #: The trigger to run the engine with. self.trigger = trigger #: A flag to toggle infinite gas self.is_test_mode = test_mode self.script_container = container #: Gas available for consumption by the engine while executing its script. self.gas_amount = self.GAS_FREE + gas self._invocation_counter: Dict[types.UInt160, int] = {} #: Notifications (Notify SYSCALLs) that occured while executing the script. self.notifications: List[Tuple[payloads.IVerifiable, types.UInt160, bytes, vm.ArrayStackItem]] = [] if self.snapshot is None or self.snapshot.persisting_block is None or self.snapshot.persisting_block.index == 0: self.exec_fee_factor = contracts.PolicyContract().DEFAULT_EXEC_FEE_FACTOR self.STORAGE_PRICE = contracts.PolicyContract().DEFAULT_STORAGE_PRICE else: self.exec_fee_factor = contracts.PolicyContract().get_exec_fee_factor(snapshot) self.STORAGE_PRICE = contracts.PolicyContract().get_storage_price(snapshot) from neo3.contracts import interop self.interop = interop
def invoke(self, engine: contracts.ApplicationEngine, version: int) -> None: """ Calls a contract function Reads the required arguments from the engine's stack and converts them to the appropiate contract function types Args: engine: the engine executing the smart contract version: which version of the smart contract to load Raises: ValueError: if the request contract version is not ValueError: if the function to be called does not exist on the contract ValueError: if trying to call a function without having the correct CallFlags """ if version != 0: raise ValueError(f"Native contract version {version} is not active" ) # type: ignore context = engine.current_context flags = contracts.CallFlags(context.call_flags) method = self._methods.get(context.ip, None) if method is None: raise ValueError( f"Method at IP \"{context.ip}\" does not exist on contract {self.service_name()}" ) if method.required_flags not in flags: raise ValueError( f"Method requires call flag: {method.required_flags} received: {flags}" ) engine.add_gas( method.cpu_price * contracts.PolicyContract().get_exec_fee_factor(engine.snapshot) + method.storage_price * contracts.PolicyContract().get_storage_price(engine.snapshot)) params: List[Any] = [] if method.add_engine: params.append(engine) if method.add_snapshot: params.append(engine.snapshot) for t in method.parameter_types: params.append( engine._stackitem_to_native(context.evaluation_stack.pop(), t)) if len(params) > 0: return_value = method.handler(*params) else: return_value = method.handler() if method.return_type is not None: context.evaluation_stack.push( engine._native_to_stackitem(return_value, type(return_value)))
def test_policy_block_account_and_is_blocked(self): engine = test_engine(has_snapshot=True) block = test_block(0) # set or we won't pass the native deploy call engine.snapshot.persisting_block = block sb = vm.ScriptBuilder() # set or we won't pass the check_comittee() in the policy contract function implementations engine.script_container = TestIVerifiable() validator = settings.standby_committee[0] script_hash = to_script_hash( contracts.Contract.create_multisig_redeemscript(1, [validator])) engine.script_container.script_hashes = [script_hash] # first we setup the stack for calling `blockAccount` # push data to create a vm.Array holding 20 bytes for the UInt160 Account parameter of the _block_account function. sb.emit_push(b'\x11' * 20) sb.emit(vm.OpCode.PUSH1) sb.emit(vm.OpCode.PACK) sb.emit_push(15) # call flags sb.emit_push("blockAccount") sb.emit_push(contracts.PolicyContract().hash.to_array()) sb.emit_syscall(syscall_name_to_int("System.Contract.Call")) # next we call `isBlocked` sb.emit_push(b'\x11' * 20) sb.emit(vm.OpCode.PUSH1) sb.emit(vm.OpCode.PACK) sb.emit_push(15) # call flags sb.emit_push("isBlocked") sb.emit_push(contracts.PolicyContract().hash.to_array()) sb.emit_syscall(syscall_name_to_int("System.Contract.Call")) script = vm.Script(sb.to_array()) engine.load_script(script) # storing the current script in a contract otherwise "System.Contract.Call" will fail its checks nef = contracts.NEF(script=sb.to_array()) manifest = contracts.ContractManifest("test_contract") sender = engine.script_container.script_hashes[0] contract = contracts.ContractState( 1, nef, manifest, 0, contract_hash(sender, nef.checksum, manifest.name)) engine.snapshot.contracts.put(contract) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(2, len(engine.result_stack)) get_is_blocked_result = engine.result_stack.pop() set_blocked_account_result = engine.result_stack.pop() self.assertTrue(set_blocked_account_result.to_boolean()) self.assertTrue(get_is_blocked_result.to_boolean())
def test_policy_set_and_get_blocked_accounts(self): engine = test_engine(has_snapshot=True) block = test_block(0) # set or we won't pass the native deploy call engine.snapshot.persisting_block = block sb = vm.ScriptBuilder() sb.emit_syscall(syscall_name_to_int("Neo.Native.Deploy")) # set or we won't pass the check_comittee() in the policy contract function implementations engine.script_container = TestIVerifiable() validator = settings.standby_committee[0] script_hash = to_script_hash( contracts.Contract.create_multisig_redeemscript(1, [validator])) engine.script_container.script_hashes = [script_hash] # first we setup the stack for calling `blockAccount` # push data to create a vm.Array holding 20 bytes for the UInt160 Account parameter of the _block_account function. sb.emit_push(b'\x11' * 20) sb.emit(vm.OpCode.PUSH1) sb.emit(vm.OpCode.PACK) sb.emit_push("blockAccount") sb.emit_push(contracts.PolicyContract().script_hash.to_array()) sb.emit_syscall(syscall_name_to_int("System.Contract.Call")) # next we call `getBlockedAccounts` sb.emit_contract_call(contracts.PolicyContract().script_hash, "getBlockedAccounts") script = vm.Script(sb.to_array()) engine.load_script(script) # storing the current script in a contract otherwise "System.Contract.Call" will fail its checks engine.snapshot.contracts.put( storage.ContractState(sb.to_array(), contracts.ContractManifest())) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(2, len(engine.result_stack)) get_blocked_accounts_result = engine.result_stack.pop() set_blocked_accounts_result = engine.result_stack.pop() self.assertTrue(set_blocked_accounts_result.to_boolean()) self.assertIsInstance(get_blocked_accounts_result, vm.InteropStackItem) stored_accounts = get_blocked_accounts_result.get_object() self.assertEqual(1, len(stored_accounts)) expected_account = types.UInt160(data=b'\x11' * 20) self.assertEqual(expected_account, stored_accounts[0])
def test_policy_unblock_account(self): # we've tested the full round trip via "System.Contract.Call" in the test # test_policy_set_and_get_blocked_accounts() # Here we take the shortcut and test the unblock account function directly engine = test_engine(has_snapshot=True) block = test_block(0) engine.snapshot.persisting_block = block engine.invoke_syscall_by_name("Neo.Native.Deploy") # we must add a script_container with valid signature to pass the check_comittee() validation check # in the function itself engine.script_container = TestIVerifiable() validator = settings.standby_committee[0] script_hash = to_script_hash( contracts.Contract.create_multisig_redeemscript(1, [validator])) engine.script_container.script_hashes = [script_hash] policy = contracts.PolicyContract() account_not_found = types.UInt160(data=b'\x11' * 20) account = types.UInt160.zero() self.assertTrue(policy._block_account(engine, account)) self.assertFalse(policy._unblock_account(engine, account_not_found)) self.assertTrue(policy._unblock_account(engine, account)) storage_key = storage.StorageKey(policy.script_hash, policy._PREFIX_BLOCKED_ACCOUNTS) storage_item = engine.snapshot.storages.try_get(storage_key) self.assertIsNotNone(storage_item) self.assertEqual(b'\x00', storage_item.value)
def test_policy_get_max_block_system_fee(self): engine = test_native_contract(contracts.PolicyContract().script_hash, "getMaxBlockSystemFee") engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack)) item = engine.result_stack.pop() self.assertEqual(vm.IntegerStackItem(900000000000), item)
def test_policy_get_max_tx_per_block(self): engine = test_native_contract(contracts.PolicyContract().script_hash, "getMaxTransactionsPerBlock") engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack)) item = engine.result_stack.pop() self.assertEqual(vm.IntegerStackItem(512), item)
def test_policy_default_get_fee_per_byte(self): engine = test_native_contract(contracts.PolicyContract().hash, "getFeePerByte") engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack)) item = engine.result_stack.pop() self.assertEqual(vm.IntegerStackItem(1000), item)
def test_native_deploy_ok(self): engine = test_engine(has_snapshot=True) block = test_block(0) engine.snapshot.persisting_block = block engine.invoke_syscall_by_name("Neo.Native.Deploy") self.assertIn("Policy", contracts.NativeContract().registered_contract_names) self.assertEqual(contracts.PolicyContract(), contracts.NativeContract.get_contract("Policy"))
def calculate_network_fee(tx: payloads.Transaction, snapshot: storage.Snapshot, account: wallet.Account) -> int: if len(tx.signers) == 0: raise ValueError("Cannot calculate the network fee without a sender in the transaction.") hashes = tx.get_script_hashes_for_verifying(snapshot) network_fee_size = (tx.HEADER_SIZE + core_utils.get_var_size(tx.signers) # type: ignore + core_utils.get_var_size(tx.attributes) # type: ignore + core_utils.get_var_size(tx.script) # type: ignore + core_utils.get_var_size(len(hashes)) # type: ignore ) exec_fee_factor = contracts.PolicyContract().get_exec_fee_factor(snapshot) network_fee = 0 for i, hash_ in enumerate(hashes): witness_script = None if hash_ == account.script_hash and account.contract and len(account.contract.script) > 0: witness_script = account.contract.script if witness_script is None and len(tx.witnesses) > 0: for witness in tx.witnesses: if witness.script_hash() == hash_: witness_script = witness.verification_script break if witness_script is None or (witness_script and len(witness_script) == 0): raise ValueError("Using a smart contract as a witness is not yet supported in mamba") elif contracts.Contract.is_signature_contract(witness_script): network_fee_size += 67 + core_utils.get_var_size(witness_script) # type: ignore network_fee = exec_fee_factor * signature_contract_costs() elif contracts.Contract.is_multisig_contract(witness_script): _, threshold, public_keys = contracts.Contract.parse_as_multisig_contract(witness_script) invocation_script_size = 66 * threshold network_fee_size += (core_utils.get_var_size(invocation_script_size) # type: ignore + invocation_script_size + core_utils.get_var_size(witness_script)) # type: ignore network_fee = exec_fee_factor * multisig_contract_costs(threshold, len(public_keys)) network_fee += network_fee_size * contracts.PolicyContract().get_fee_per_byte(snapshot) return network_fee
def init(self): self._methods: Dict[int, _NativeMethodMeta] = {} # offset, meta self._management = contracts.ManagementContract() self._neo = contracts.NeoToken() self._gas = contracts.GasToken() self._policy = contracts.PolicyContract() self._oracle = contracts.OracleContract() self._ledger = contracts.LedgerContract() self._role = contracts.DesignationContract() self._crypto = contracts.CryptoContract() self._stdlib = contracts.StdLibContract() # Find all methods that have been augmented by the @register decorator # and turn them into methods that can be called by VM scripts methods_meta = [] for pair in inspect.getmembers(self, lambda m: hasattr(m, "native_call")): methods_meta.append(_NativeMethodMeta(pair[1])) methods_meta.sort( key=lambda x: (x.descriptor.name, len(x.descriptor.parameters))) sb = vm.ScriptBuilder() for meta in methods_meta: meta.descriptor.offset = len(sb) sb.emit_push(0) self._methods.update({len(sb): meta}) sb.emit_syscall(1736177434) # "System.Contract.CallNative" sb.emit(vm.OpCode.RET) self._script: bytes = sb.to_array() self.nef = contracts.NEF("neo-core-v3.0", self._script) sender = types.UInt160.zero() # OpCode.PUSH1 sb = vm.ScriptBuilder() sb.emit(vm.OpCode.ABORT) sb.emit_push(sender.to_array()) sb.emit_push(0) sb.emit_push(self.service_name()) self._hash: types.UInt160 = to_script_hash(sb.to_array()) self._manifest: contracts.ContractManifest = contracts.ContractManifest( ) self._manifest.name = self.service_name() self._manifest.abi.methods = list( map(lambda m: m.descriptor, methods_meta)) if self._id != NativeContract._id: self._contracts.update({self.service_name(): self}) self._contract_hashes.update({self._hash: self}) self.active_block_index = settings.native_contract_activation.get( self.service_name, 0)
def test_policy_setters_fail_without_signatures(self): # cover set functions where check_committee fails policy = contracts.PolicyContract() engine = test_engine(has_snapshot=True) block = test_block(0) engine.snapshot.persisting_block = block engine.script_container = TestIVerifiable() with self.assertRaises(ValueError) as context: policy._set_fee_per_byte(engine, 0) self.assertEqual("Check committee failed", str(context.exception)) self.assertFalse(policy._block_account(engine, None)) self.assertFalse(policy._unblock_account(engine, None))
def test_policy_setters_fail_without_signatures(self): # cover set functions where check_committee fails policy = contracts.PolicyContract() engine = test_engine(has_snapshot=True) block = test_block(0) engine.snapshot.persisting_block = block engine.invoke_syscall_by_name("Neo.Native.Deploy") engine.script_container = TestIVerifiable() self.assertFalse(policy._set_max_block_size(engine, None)) self.assertFalse(policy._set_max_transactions_per_block(engine, None)) self.assertFalse(policy._set_max_block_system_fee(engine, None)) self.assertFalse(policy._set_fee_per_byte(engine, None)) self.assertFalse(policy._block_account(engine, None)) self.assertFalse(policy._unblock_account(engine, None))
def test_native_call(self): engine = test_engine(has_snapshot=True, default_script=True) block = test_block(0) engine.snapshot.persisting_block = block # need to create and store a contract matching the current_context.script # otherwise system.contract.call checks will fail engine.snapshot.contracts.put( storage.ContractState(b'\x40', contracts.ContractManifest())) engine.invoke_syscall_by_name("Neo.Native.Deploy") engine.push(vm.ArrayStackItem( engine.reference_counter)) # empty array for no arguments engine.push(vm.ByteStringStackItem(b'getMaxTransactionsPerBlock')) policy_contract_hash = vm.ByteStringStackItem( contracts.PolicyContract().script_hash.to_array()) engine.push(policy_contract_hash) engine.invoke_syscall_by_name("System.Contract.Call")
def test_policy_unblock_account(self): engine = test_engine(has_snapshot=True) block = test_block(0) engine.snapshot.persisting_block = block # we must add a script_container with valid signature to pass the check_comittee() validation check # in the function itself engine.script_container = TestIVerifiable() validator = settings.standby_committee[0] script_hash = to_script_hash( contracts.Contract.create_multisig_redeemscript(1, [validator])) engine.script_container.script_hashes = [script_hash] policy = contracts.PolicyContract() account_not_found = types.UInt160(data=b'\x11' * 20) account = types.UInt160.zero() self.assertTrue(policy._block_account(engine, account)) self.assertFalse(policy._unblock_account(engine, account_not_found)) self.assertTrue(policy._unblock_account(engine, account)) storage_key = policy.key_blocked_account + account storage_item = engine.snapshot.storages.try_get(storage_key) self.assertIsNone(storage_item)
def test_policy_limit_setters(self): policy = contracts.PolicyContract() D = namedtuple( 'D', ['test_func', 'value', 'expected_return', 'storage_prefix']) testdata = [ D(policy._set_max_block_size, message.Message.PAYLOAD_MAX_SIZE, False, policy._PREFIX_MAX_BLOCK_SIZE), D(policy._set_max_block_size, 123, True, policy._PREFIX_MAX_BLOCK_SIZE), D(policy._set_max_transactions_per_block, 123, True, policy._PREFIX_MAX_TRANSACTIONS_PER_BLOCK), D(policy._set_max_block_system_fee, 123, False, policy._PREFIX_MAX_BLOCK_SYSTEM_FEE), # value is lower than magic number D(policy._set_max_block_system_fee, 5_000_000, True, policy._PREFIX_MAX_BLOCK_SYSTEM_FEE), D(policy._set_fee_per_byte, 123, True, policy._PREFIX_FEE_PER_BYTE) ] engine = test_engine(has_snapshot=True) block = test_block(0) engine.snapshot.persisting_block = block engine.invoke_syscall_by_name("Neo.Native.Deploy") # set or we won't pass the check_comittee() in the policy contract function implementations engine.script_container = TestIVerifiable() validator = settings.standby_committee[0] script_hash = to_script_hash( contracts.Contract.create_multisig_redeemscript(1, [validator])) engine.script_container.script_hashes = [script_hash] for d in testdata: self.assertEqual(d.expected_return, d.test_func(engine, d.value)) if d.expected_return is True: item = engine.snapshot.storages.try_get( storage.StorageKey(policy.script_hash, d.storage_prefix)) self.assertIsNotNone(item) self.assertEqual(d.value, int.from_bytes(item.value, 'little'))
def test_various(self): native = contracts.NativeContract() known_contracts = native.registered_contracts self.assertIn(contracts.GasToken(), known_contracts) self.assertIn(contracts.NeoToken(), known_contracts) self.assertIn(contracts.PolicyContract(), known_contracts)
def test_basics(self): policy = contracts.PolicyContract() self.assertEqual(-3, policy.id) self.assertEqual("Policy", contracts.PolicyContract.service_name()) self.assertEqual([], policy.supported_standards())
def test_basics(self): policy = contracts.PolicyContract() self.assertEqual(-7, policy.id) self.assertEqual("PolicyContract", contracts.PolicyContract().service_name())