def test_contract_call(self): engine = test_engine(has_snapshot=True, default_script=False) # current executing contract fake_hash = types.UInt160.deserialize_from_bytes(b'\x01' * 20) contract = contracts.ContractState(0, hello_world_nef, hello_world_manifest, 0, fake_hash) engine.snapshot.contracts.put(contract) # target contract fake_hash2 = types.UInt160.deserialize_from_bytes(b'\x02' * 20) target_contract = contracts.ContractState(1, contract3_nef, contract3_manifest, 0, fake_hash2) engine.snapshot.contracts.put(target_contract) engine.load_script(vm.Script(contract.script)) array = vm.ArrayStackItem(engine.reference_counter) array.append(vm.IntegerStackItem(3)) engine.push(array) # args engine.push(vm.IntegerStackItem(15)) # callflags engine.push(vm.ByteStringStackItem("test_func2")) # method engine.push(vm.ByteStringStackItem(target_contract.hash.to_array())) engine.invoke_syscall_by_name("System.Contract.Call") engine.execute() self.assertEqual(2, len(engine.result_stack)) main_contract_return_value = engine.result_stack.pop() syscall_called_contract_return_value = engine.result_stack.pop() self.assertEqual("hello world", main_contract_return_value.to_array().decode()) self.assertEqual(4, int(syscall_called_contract_return_value))
def test_equals(self): manifest = contracts.ContractManifest() nef = contracts.NEF() state = contracts.ContractState(1, nef, manifest, 0, types.UInt160.zero()) clone = state.clone() self.assertEqual(state, clone) nef2 = contracts.NEF() state2 = contracts.ContractState(2, nef2, manifest, 0, types.UInt160(b'\x01' * 20)) self.assertNotEqual(state, state2) self.assertNotEqual(state, None) self.assertNotEqual(state, object())
def test_getcallingscripthash(self): """ Testing this requires 2 contracts 1) caller_contract: uses a System.Contract.Call to call callee_contract. This will set the calling script hash on the ExecutionContext of the callee_contract 2) callee_contract: uses a System.Runtime.GetCallingScriptHash to return the calling script """ sb = vm.ScriptBuilder() sb.emit_syscall( syscall_name_to_int("System.Runtime.GetCallingScriptHash")) callee_contract_script = sb.to_array() callee_nef = contracts.NEF(script=callee_contract_script) callee_contract_name = "callee_contract" callee_manifest = contracts.ContractManifest(callee_contract_name) callee_manifest.abi.methods = [ contracts.ContractMethodDescriptor( "test_func", 0, [], contracts.ContractParameterType.ANY, True) ] callee_contract_hash = contract_hash(types.UInt160.zero(), callee_nef.checksum, callee_contract_name) callee_contract = contracts.ContractState(1, callee_nef, callee_manifest, 0, callee_contract_hash) # create caller_contract script sb = vm.ScriptBuilder() sb.emit_dynamic_call(callee_contract.hash, "test_func") caller_script = sb.to_array() caller_nef = contracts.NEF(script=caller_script) caller_contract_name = "caller_contract" caller_manifest = contracts.ContractManifest(caller_contract_name) caller_contract_hash = contract_hash(types.UInt160.zero(), caller_nef.checksum, caller_contract_name) caller_contract = contracts.ContractState(2, caller_nef, caller_manifest, 0, caller_contract_hash) engine = test_engine(has_snapshot=True, default_script=False) engine.snapshot.contracts.put(callee_contract) engine.snapshot.contracts.put(caller_contract) engine.load_script(vm.Script(caller_script)) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack)) item = engine.result_stack.pop() self.assertEqual( to_script_hash(caller_nef.script).to_array(), item.to_array())
def test_transfer_partial_balance_to_account_with_balance(self): gas = contracts.GasToken() manifest = contracts.ContractManifest("contract_name") nef = contracts.NEF(script=b'\x40') state_to = contracts.ContractState( 1, nef, manifest, 0, contract_hash(types.UInt160.zero(), nef.checksum, manifest.name)) account_to = state_to.hash storage_key_to = gas.key_account + account_to account_to_state = gas._state() account_to_state.balance = vm.BigInteger(100) storage_item_to = storage.StorageItem(account_to_state.to_array()) account_from = types.UInt160(b'\x01' * 20) storage_key_from = gas.key_account + account_from account_from_state = gas._state() account_from_state.balance = vm.BigInteger(123) storage_item_from = storage.StorageItem(account_from_state.to_array()) amount = vm.BigInteger(50) engine = self.transfer_helper(gas, account_from, account_to, amount) # 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) transfer_event = () def notify_listener(contract_script_hash, event, state): nonlocal transfer_event transfer_event = (contract_script_hash, event, state) msgrouter.interop_notify += notify_listener engine.execute() self.assertEqual(1, len(engine.result_stack)) result = engine.result_stack.pop() self.assertTrue(result) self.assertEqual(gas.hash, transfer_event[0]) self.assertEqual("Transfer", transfer_event[1]) state_items = list(transfer_event[2]) self.assertEqual(account_from, types.UInt160(state_items[0].to_array())) self.assertEqual(account_to, types.UInt160(state_items[1].to_array())) self.assertEqual(amount, state_items[2].to_biginteger()) # validate from account is deducted by `amount` new_storage_account_from = engine.snapshot.storages.get( storage_key_from) new_account_state_from = gas._state.deserialize_from_bytes( new_storage_account_from.value) self.assertEqual(account_from_state.balance - amount, new_account_state_from.balance) # validate to account is credited with `amount` new_storage_account_to = engine.snapshot.storages.get(storage_key_to) new_account_state_to = gas._state.deserialize_from_bytes( new_storage_account_to.value) self.assertEqual(account_to_state.balance + amount, new_account_state_to.balance)
def transfer_helper(self, contract: contracts.NativeContract, from_account: types.UInt160, to_account: types.UInt160, amount: vm.BigInteger): 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 engine.invocation_stack.pop() # we no longer need the default script engine.script_container = TestIVerifiable() engine.script_container.script_hashes = [from_account] sb = vm.ScriptBuilder() sb.emit(vm.OpCode.PUSHNULL) sb.emit_push(amount) sb.emit_push(to_account.to_array()) sb.emit_push(from_account.to_array()) sb.emit_push(4) sb.emit(vm.OpCode.PACK) sb.emit_push(15) # callflags sb.emit_push(b'transfer') sb.emit_push(contract.hash.to_array()) sb.emit_syscall(syscall_name_to_int("System.Contract.Call")) engine.load_script(vm.Script(sb.to_array())) nef = contracts.NEF(script=sb.to_array()) manifest = contracts.ContractManifest("test_contract") contract_state = contracts.ContractState( 1, nef, manifest, 0, contract_hash(from_account, nef.checksum, manifest.name)) engine.snapshot.contracts.put(contract_state) return engine
def test_is_allowed_based_on_group(self): # We test the group permissions where all methods are allowed to be called # if the 'groups' member is valid. private_key = b'\x02\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\x01' keypair = cryptography.KeyPair(private_key) dummy_contract_hash = types.UInt160.from_string("01" * 20) contract_state = contracts.ContractState(1, contracts.NEF(), contracts.ContractManifest(), 0, dummy_contract_hash) signature = cryptography.sign(contract_state.hash.to_array(), keypair.private_key) contract_state.manifest.groups = [ contracts.ContractGroup(keypair.public_key, signature) ] cpd = contracts.ContractPermissionDescriptor(group=keypair.public_key) cp = contracts.ContractPermission( contract=cpd, methods=contracts.WildcardContainer.create_wildcard()) self.assertTrue(cp.is_allowed(contract_state, "dummy_method")) # now modify the manifest to have a different `groups` attribute such that validation fails public_key = cryptography.ECPoint.deserialize_from_bytes( b'\x00') # ECPoint.Infinity contract_state.manifest.groups = [ contracts.ContractGroup(public_key, signature) ] self.assertFalse(cp.is_allowed(contract_state, "dummy_method"))
def test_storage_get_key_not_found(self): engine = test_engine(has_snapshot=True, has_container=True) script = vm.ScriptBuilder() # key parameter for the `Storage.Get` syscall script.emit(vm.OpCode.PUSH2) script.emit_syscall(syscall_name_to_int("System.Storage.GetContext")) # at this point our stack looks like follows # * storage context # * key script.emit_syscall(syscall_name_to_int("System.Storage.Get")) engine.load_script(vm.Script(script.to_array())) # we have to store our contract or some sanity checks will fail (like getting a StorageContext nef = contracts.NEF(script=script.to_array()) contract = contracts.ContractState(1, nef, self.manifest, 0, to_script_hash(nef.script)) engine.snapshot.contracts.put(contract) storage_key = storage.StorageKey(contract.id, b'\x01') storage_item = storage.StorageItem(b'\x11') engine.snapshot.storages.put(storage_key, storage_item) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack)) item = engine.result_stack.pop() self.assertIsInstance(item, vm.NullStackItem)
def test_storage_put_new(self): # see `test_storage_get_key_not_found()` for a description on why the storage is setup with a script as is # setup engine = test_engine(has_snapshot=True) script = vm.ScriptBuilder() script.emit(vm.OpCode.PUSH2) # storage put value script.emit(vm.OpCode.PUSH1) # storage put key script.emit_syscall(syscall_name_to_int("System.Storage.GetContext")) script.emit_syscall(syscall_name_to_int("System.Storage.Put")) engine.load_script(vm.Script(script.to_array())) nef = contracts.NEF(script=script.to_array()) manifest = contracts.ContractManifest(f"contractname1") manifest.abi.methods = [ contracts.ContractMethodDescriptor( "test_func", 0, [], contracts.ContractParameterType.ANY, True) ] hash_ = to_script_hash(nef.script) contract = contracts.ContractState(1, nef, manifest, 0, hash_) engine.snapshot.contracts.put(contract) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) storage_key = storage.StorageKey(1, b'\x01') item = engine.snapshot.storages.try_get(storage_key) self.assertIsNotNone(item) self.assertEqual(b'\x02', item.value)
def test_storage_get_ok2(self): # this is basically the same as `test_storage_get_ok` # but performed by executing a script # it exists to validate that the `Optional[bytes]` return value is converted properly engine = test_engine(has_snapshot=True) script = vm.ScriptBuilder() script.emit(vm.OpCode.PUSH1) script.emit_syscall(syscall_name_to_int("System.Storage.GetContext")) script.emit_syscall(syscall_name_to_int("System.Storage.Get")) engine.load_script(vm.Script(script.to_array())) nef = contracts.NEF(script=script.to_array()) contract_hash = to_script_hash(nef.script) contract = contracts.ContractState(1, nef, self.manifest, 0, contract_hash) engine.snapshot.contracts.put(contract) storage_key = storage.StorageKey(contract.id, b'\x01') storage_item = storage.StorageItem(b'\x11') engine.snapshot.storages.put(storage_key, storage_item) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack)) item = engine.result_stack.pop() self.assertEqual(storage_item.value, item.to_array())
def test_contractstate_clone(self): manifest = contracts.ContractManifest() nef = contracts.NEF() state = contracts.ContractState(1, nef, manifest, 0, types.UInt160.zero()) clone = state.clone() self.assertNotEqual(id(state), id(clone)) self.assertNotEqual(id(state.manifest), id(clone.manifest))
def test_get_invocation_counter_ok(self): """ We need to setup 2 contracts 1) caller_contract: uses a System.Contract.Call to call callee_contract. This will increase the invocation counter of the callee contract 2) callee_contract: uses a System.Runtime.GetInvocationCounter """ engine = test_engine(has_snapshot=True, has_container=False, default_script=False) sb = vm.ScriptBuilder() sb.emit_syscall( syscall_name_to_int("System.Runtime.GetInvocationCounter")) callee_contract_script = sb.to_array() callee_nef = contracts.NEF(script=callee_contract_script) callee_manifest = contracts.ContractManifest("contract1") callee_manifest.abi.methods = [ contracts.ContractMethodDescriptor( "test_func", 0, [], contracts.ContractParameterType.ANY, True) ] callee_contract = contracts.ContractState( 1, callee_nef, callee_manifest, 0, contract_hash(types.UInt160.zero(), callee_nef.checksum, callee_manifest.name)) sb = vm.ScriptBuilder() sb.emit_dynamic_call(callee_contract.hash, "test_func") caller_script = sb.to_array() caller_nef = contracts.NEF(script=caller_script) caller_manifest = contracts.ContractManifest("contract2") caller_contract = contracts.ContractState( 2, caller_nef, caller_manifest, 0, contract_hash(types.UInt160.zero(), caller_nef.checksum, caller_manifest.name)) engine.snapshot.contracts.put(callee_contract) engine.snapshot.contracts.put(caller_contract) engine.load_script(vm.Script(caller_script)) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack)) item = engine.result_stack.pop() self.assertEqual(1, int(item))
def test_checkwitness_called_by_entry(self): """ We need to setup 2 contracts 1) caller_contract: uses a System.Contract.Call to call callee_contract. This will set the calling script hash on the ExecutionContext of the callee_contract 2) callee_contract: uses a System.Runtime.CheckWitness """ engine = test_engine(has_snapshot=True, has_container=False, default_script=False) tx = test_tx() tx.signers[0].scope = payloads.WitnessScope.CALLED_BY_ENTRY engine.script_container = tx sb = vm.ScriptBuilder() sb.emit_push(tx.sender.to_array()) sb.emit_syscall(syscall_name_to_int("System.Runtime.CheckWitness")) callee_contract_script = sb.to_array() callee_nef = contracts.NEF(script=callee_contract_script) callee_manifest = contracts.ContractManifest() callee_manifest.abi.methods = [ contracts.ContractMethodDescriptor( "test_func", 0, [], contracts.ContractParameterType.ANY, True) ] callee_contract = contracts.ContractState( 1, callee_nef, callee_manifest, 0, to_script_hash(callee_contract_script)) sb = vm.ScriptBuilder() sb.emit_dynamic_call(callee_contract.hash, "test_func") caller_script = sb.to_array() caller_nef = contracts.NEF(script=caller_script) caller_manifest = contracts.ContractManifest() caller_contract = contracts.ContractState( 2, caller_nef, caller_manifest, 0, to_script_hash(caller_script)) engine.snapshot.contracts.put(callee_contract) engine.snapshot.contracts.put(caller_contract) engine.load_script(vm.Script(caller_script)) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack)) item = engine.result_stack.pop() self.assertTrue(item.to_boolean())
def setUp(self) -> None: self.RET = b'\x40' self.manifest = contracts.ContractManifest("contract_name") self.nef = contracts.NEF(script=self.RET) self.contract_hash = to_script_hash(self.nef.script) self.contract = contracts.ContractState(1, self.nef, self.manifest, 0, self.contract_hash) self.contract.manifest.abi.methods = [ contracts.ContractMethodDescriptor( "test_func", 0, [], contracts.ContractParameterType.ANY, True) ]
def contract_create_with_data( self, engine: contracts.ApplicationEngine, nef_file: bytes, manifest: bytes, data: vm.StackItem) -> contracts.ContractState: 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( max(engine.STORAGE_PRICE * (nef_len + manifest_len), self.get_minimum_deployment_fee(engine.snapshot))) nef = contracts.NEF.deserialize_from_bytes(nef_file) parsed_manifest = contracts.ContractManifest.from_json( json.loads(manifest.decode())) self.validate(nef.script, parsed_manifest.abi) sb = vm.ScriptBuilder() sb.emit(vm.OpCode.ABORT) sb.emit_push(engine.script_container.sender.to_array()) sb.emit_push(nef.checksum) sb.emit_push(parsed_manifest.name) hash_ = to_script_hash(sb.to_array()) existing_contract = engine.snapshot.contracts.try_get(hash_) if existing_contract is not None: raise ValueError("Contract already exists") contract = contracts.ContractState( self.get_next_available_id(engine.snapshot), nef, parsed_manifest, 0, hash_) if not contract.manifest.is_valid(hash_): raise ValueError("Error: invalid manifest") engine.snapshot.contracts.put(contract) method_descriptor = contract.manifest.abi.get_method("_deploy", 2) if method_descriptor is not None: engine.call_from_native(self.hash, hash_, method_descriptor.name, [data, vm.BooleanStackItem(False)]) msgrouter.interop_notify( self.hash, "Deploy", vm.ArrayStackItem(engine.reference_counter, vm.ByteStringStackItem( contract.hash.to_array()))) return contract
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 on_persist(self, engine: contracts.ApplicationEngine) -> None: # NEO implicitely expects a certain order of contract initialization # Native contracts have negative values for `id`, so we reverse the results sorted_contracts = sorted(self.registered_contracts, key=lambda contract: contract.id, reverse=True) for contract in sorted_contracts: if contract.active_block_index != engine.snapshot.persisting_block.index: continue engine.snapshot.contracts.put( contracts.ContractState(contract.id, contract.nef, contract.manifest, 0, contract.hash)) contract._initialize(engine)
def test_transfer_from_empty_account(self): gas = contracts.GasToken() manifest = contracts.ContractManifest("test_contract2") nef = contracts.NEF(script=b'\x40') state = contracts.ContractState( 2, nef, manifest, 0, contract_hash(types.UInt160.zero(), nef.checksum, manifest.name)) engine = self.transfer_helper(gas, types.UInt160.zero(), state.hash, vm.BigInteger(1)) engine.snapshot.contracts.put(state) engine.execute() self.assertEqual(1, len(engine.result_stack)) result = engine.result_stack.pop() self.assertFalse(result)
def deploy_another_contract(self, nef_path: str, manifest_path: str = '') -> UInt160: """ these extra contracts can be called but cannot be tested """ raw_nef, raw_manifest = self.read_raw_nef_and_raw_manifest( nef_path, manifest_path) nef, manifest = self.build_nef_and_manifest_from_raw( raw_nef, raw_manifest) contract_hash = types.UInt160.deserialize_from_bytes(raw_nef[-20:]) contract = contracts.ContractState(self.next_contract_id, nef, manifest, 0, contract_hash) self.previous_engine.snapshot.contracts.put(contract) self.deployed_contracts.append(contract) self.next_contract_id += 1 return contract_hash
def test_is_allowed_invalid_method(self): # in the above tests we validated the 'group' and 'contract_hash' matching logic # now we validate 'method' matching dummy_contract_hash = types.UInt160.from_string("01" * 20) contract_state = contracts.ContractState(1, contracts.NEF(), contracts.ContractManifest(), 0, dummy_contract_hash) # setup an allowed permission for a contract with UInt160.zero hash for 2 methods cpd = contracts.ContractPermissionDescriptor( contract_hash=dummy_contract_hash) cp = contracts.ContractPermission( contract=cpd, methods=contracts.WildcardContainer(data=['method1', 'method2'])) self.assertTrue(cp.is_allowed(contract_state, "method1")) self.assertTrue(cp.is_allowed(contract_state, "method2")) self.assertFalse(cp.is_allowed(contract_state, "method3"))
def __init__(self, nef_path: str, manifest_path: str = '', signers: List[Union[str, UInt160, payloads.Signer]] = None, scope: payloads.WitnessScope = payloads.WitnessScope.GLOBAL): """ Only the contract specified in __init__ can be tested. You can deploy more contracts to be called by the tested contract. """ self.Prefix_Account_bytes = EngineResultInterpreter.int_to_bytes( self.Prefix_Account) self.raw_nef, self.raw_manifest = self.read_raw_nef_and_raw_manifest( nef_path, manifest_path) self.nef, self.manifest = self.build_nef_and_manifest_from_raw( self.raw_nef, self.raw_manifest) self.previous_engine: ApplicationEngine = self.new_engine() self.previous_processed_result = None self.contract = contracts.ContractState( 0, self.nef, self.manifest, 0, types.UInt160.deserialize_from_bytes(self.raw_nef)) self.next_contract_id = 1 # if you deploy more contracts in a same engine, the contracts must have different id self.previous_engine.snapshot.contracts.put(self.contract) self.deployed_contracts = [self.contract] if signers: signers = list( map(lambda signer: self.signer_auto_checker(signer, scope), signers)) self.signers = signers else: self.signers = [] # add methods to this class for users to call contract methods for method in self.manifest.abi.methods: method_name = method.name method_name_with_print = method_name + '_with_print' if hasattr(self, method_name) or hasattr(self, method_name_with_print): print( f'Warning: method {method_name} or {method_name_with_print} already exists in {self}' ) partial_function = partial(self.invoke_method, method_name) setattr(self, method_name, partial_function) partial_function_with_print = partial( self.invoke_method_with_print, method_name) setattr(self, method_name_with_print, partial_function_with_print)
def test_transfer_full_balance(self): gas = contracts.GasToken() manifest = contracts.ContractManifest("contract_name_to") nef = contracts.NEF(script=b'\x40') state_to = contracts.ContractState( 1, nef, manifest, 0, contract_hash(types.UInt160.zero(), nef.checksum, manifest.name)) account_to = state_to.hash account_from = types.UInt160(b'\x01' * 20) storage_key_from = gas.key_account + account_from account_from_state = gas._state() account_from_state.balance = vm.BigInteger(123) storage_item_from = storage.StorageItem(account_from_state.to_array()) amount = account_from_state.balance engine = self.transfer_helper(gas, account_from, account_to, amount) # ensure the source account has balance engine.snapshot.storages.put(storage_key_from, storage_item_from) transfer_event = () def notify_listener(contract_script_hash, event, state): nonlocal transfer_event transfer_event = (contract_script_hash, event, state) msgrouter.interop_notify += notify_listener engine.execute() self.assertEqual(1, len(engine.result_stack)) result = engine.result_stack.pop() self.assertTrue(result) self.assertEqual(gas.hash, transfer_event[0]) self.assertEqual("Transfer", transfer_event[1]) state_items = list(transfer_event[2]) self.assertEqual(account_from, types.UInt160(state_items[0].to_array())) self.assertEqual(account_to, types.UInt160(state_items[1].to_array())) self.assertEqual(amount, state_items[2].to_biginteger()) # test that the source account is no longer present in storage as the balance is zero self.assertIsNone(engine.snapshot.storages.try_get(storage_key_from))
def test_is_allowed_based_on_hash(self): # We test the group permissions where all methods are allowed to be called # if contract_hash is valid. dummy_contract_hash = types.UInt160.zero() contract_state = contracts.ContractState(1, contracts.NEF(), contracts.ContractManifest(), 0, dummy_contract_hash) # setup an allowed permission for a contract with UInt160.zero hash for all methods cpd = contracts.ContractPermissionDescriptor( contract_hash=dummy_contract_hash) cp = contracts.ContractPermission( contract=cpd, methods=contracts.WildcardContainer.create_wildcard()) self.assertTrue(cp.is_allowed(contract_state, "dummy_method")) # now create a different contract hash and verify it does not give permission contract_state.hash = types.UInt160(b'\x01' * 20) self.assertFalse(cp.is_allowed(contract_state, "dummy_method"))
def test_native_contract(contract_hash: types.UInt160, operation: str, args=None): engine = test_engine(has_snapshot=True) block = test_block(0) # or we won't pass the native deploy call engine.snapshot.persisting_block = block sb = vm.ScriptBuilder() sb.emit_dynamic_call(contract_hash, operation) 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") next_id = contracts.ManagementContract().get_next_available_id( engine.snapshot) contract = contracts.ContractState(next_id + 1, nef, manifest, 0, to_script_hash(nef.script)) engine.snapshot.contracts.put(contract) return engine
def test_transfer_more_than_balance(self): gas = contracts.GasToken() account_from = types.UInt160.zero() storage_key_from = gas.key_account + account_from account_state = gas._state() account_state.balance = vm.BigInteger(123) storage_item_from = storage.StorageItem(account_state.to_array()) manifest_to = contracts.ContractManifest("source_contract") nef_to = contracts.NEF(script=b'\x40') state_to = contracts.ContractState( 1, nef_to, manifest_to, 0, contract_hash(types.UInt160.zero(), nef_to.checksum, manifest_to.name)) account_to = state_to.hash amount = account_state.balance + 1 engine = self.transfer_helper(gas, account_from, account_to, amount) # ensure the source account has balance engine.snapshot.storages.put(storage_key_from, storage_item_from) engine.execute() self.assertEqual(1, len(engine.result_stack)) result = engine.result_stack.pop() self.assertFalse(result)
def test_contract_call_exceptions(self): engine = test_engine(has_snapshot=True, default_script=False) engine.load_script(vm.Script(hello_world_nef.script)) # can't find contract with self.assertRaises(ValueError) as context: engine._contract_call_internal( types.UInt160.zero(), "valid_method", contracts.CallFlags.ALL, False, vm.ArrayStackItem(engine.reference_counter)) self.assertEqual("[System.Contract.Call] Can't find target contract", str(context.exception)) fake_contract_hash = types.UInt160(b'\x01' * 20) target_contract = contracts.ContractState(0, contract3_nef, contract3_manifest, 0, fake_contract_hash) engine.snapshot.contracts.put(target_contract) # modify the manifest of the current executing contract to only allow to call 1 specific method on other contracts new_current_manifest = deepcopy(hello_world_manifest) new_current_manifest.permissions = [ contracts.ContractPermission( contracts.ContractPermissionDescriptor( ), # allow to call any contract contracts.WildcardContainer( ['method_aaaa']) # allowing to call the listed method only ) ] fake_contract_hash2 = types.UInt160(b'\x02' * 20) new_current_contract = contracts.ContractState(1, hello_world_nef, new_current_manifest, 0, fake_contract_hash2) engine.snapshot.contracts.put(new_current_contract) with self.assertRaises(ValueError) as context: engine._contract_call_internal( target_contract.hash, "invalid_method", contracts.CallFlags.ALL, False, vm.ArrayStackItem(engine.reference_counter)) self.assertEqual( "[System.Contract.Call] Method 'invalid_method' with 0 arguments does not exist on target contract", str(context.exception)) # restore current contract to its original form and try to call a non-existing contract current_contract = contracts.ContractState(1, hello_world_nef, hello_world_manifest, 1, fake_contract_hash2) engine.snapshot.contracts.delete(new_current_contract.hash) engine.snapshot.contracts.put(current_contract) with self.assertRaises(ValueError) as context: engine._contract_call_internal( target_contract.hash, "invalid_method", contracts.CallFlags.ALL, False, vm.ArrayStackItem(engine.reference_counter)) self.assertEqual( "[System.Contract.Call] Method 'invalid_method' with 0 arguments does not exist on target contract", str(context.exception)) # call the target method with invalid number of arguments array = vm.ArrayStackItem(engine.reference_counter) array.append([vm.NullStackItem(), vm.NullStackItem()]) with self.assertRaises(ValueError) as context: engine._contract_call_internal(target_contract.hash, "test_func", contracts.CallFlags.ALL, False, array) self.assertEqual( "[System.Contract.Call] Method 'test_func' with 2 arguments does not exist on target contract", str(context.exception))
def test_checkwitness_custom_groups(self): """ We need to setup 2 contracts 1) caller_contract: uses a System.Contract.Call to call callee_contract. This will set the calling script hash on the ExecutionContext of the callee_contract 2) callee_contract: uses a System.Runtime.CheckWitness """ engine = test_engine(has_snapshot=True, default_script=False) tx = test_tx() engine.script_container = tx ''' Create the callee contract, which does a checkwitness on its caller. Checkwitness shall only pass if the caller == "caller_contract" as defined by CustomGroup ''' sb = vm.ScriptBuilder() sb.emit_syscall( syscall_name_to_int("System.Runtime.GetCallingScriptHash") ) # get script hash of "caller_contract" sb.emit_syscall(syscall_name_to_int("System.Runtime.CheckWitness")) callee_contract_script = sb.to_array() callee_nef = contracts.NEF(script=callee_contract_script) callee_contract_name = "callee_contract" callee_manifest = contracts.ContractManifest(callee_contract_name) callee_manifest.abi.methods = [ contracts.ContractMethodDescriptor( "test_func", 0, [], contracts.ContractParameterType.ANY, True) ] callee_contract_hash = contract_hash(tx.sender, callee_nef.checksum, callee_contract_name) callee_contract = contracts.ContractState(1, callee_nef, callee_manifest, 0, callee_contract_hash) sb = vm.ScriptBuilder() sb.emit_dynamic_call(callee_contract.hash, "test_func") caller_script = sb.to_array() caller_nef = contracts.NEF(script=caller_script) caller_contract_name = "caller_contract" caller_manifest = contracts.ContractManifest(caller_contract_name) keypair = cryptography.KeyPair(private_key=b'\x01' * 32) signature = cryptography.sign(caller_script, keypair.private_key) caller_manifest.groups = [ contracts.ContractGroup(public_key=keypair.public_key, signature=signature) ] caller_contract_hash = contract_hash(tx.sender, caller_nef.checksum, caller_contract_name) caller_contract = contracts.ContractState(2, caller_nef, caller_manifest, 0, caller_contract_hash) engine.snapshot.contracts.put(caller_contract) engine.snapshot.contracts.put(callee_contract) engine.load_script(vm.Script(caller_script)) tx.signers[0].scope = payloads.WitnessScope.CUSTOM_GROUPS tx.signers[0].allowed_groups = [keypair.public_key] engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack)) item = engine.result_stack.pop() self.assertTrue(item.to_boolean()) # now try again but make sure it fails if the public key is not listed in the allowed groups engine = test_engine(has_snapshot=True, default_script=False) tx = test_tx() engine.script_container = tx engine.snapshot.contracts.put(caller_contract) engine.snapshot.contracts.put(callee_contract) engine.load_script(vm.Script(caller_script)) tx.signers[0].scope = payloads.WitnessScope.CUSTOM_GROUPS keypair = cryptography.KeyPair(private_key=b'\x02' * 32) tx.signers[0].allowed_groups = [keypair.public_key] engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack)) item = engine.result_stack.pop() self.assertFalse(item.to_boolean())