コード例 #1
0
    def test_contract_create_already_exits(self):
        engine = test_engine(has_snapshot=True)

        # store store the contract ourselves
        contract = storage.ContractState(hello_world_nef.script,
                                         hello_world_manifest)
        engine.snapshot.contracts.put(contract)
        # now try to create a contract
        engine.push(vm.ByteStringStackItem(str(hello_world_manifest).encode()))
        engine.push(vm.ByteStringStackItem(hello_world_nef.script))
        with self.assertRaises(ValueError) as context:
            engine.invoke_syscall_by_name("System.Contract.Create")
        self.assertEqual("Contract already exists", str(context.exception))
コード例 #2
0
    def test_contract_is_standard_ok(self):
        keypair = cryptography.KeyPair(b'\x01' * 32)
        sig_contract = contracts.Contract.create_signature_contract(
            keypair.public_key)

        engine = test_engine(has_snapshot=True)
        contract = storage.ContractState(
            sig_contract.script,
            contracts.ContractManifest(sig_contract.script_hash))
        engine.snapshot.contracts.put(contract)
        engine.push(vm.ByteStringStackItem(contract.script_hash().to_array()))
        engine.invoke_syscall_by_name("System.Contract.IsStandard")
        engine.execute()
        self.assertEqual(True, engine.result_stack.pop().to_boolean())
コード例 #3
0
    def test_contract_destroy_not_found(self):
        # contract destroy must be called by the contract itself, the function uses the engine.current_script hash for
        # locating the contract to delete. We set the engine to have a default script, which does not match a contract
        # that exists in storage
        engine = test_engine(has_snapshot=True, default_script=True)
        # we also place at least 1 contract in storage and validate it doesn't get removed
        contract = storage.ContractState(hello_world_nef.script,
                                         hello_world_manifest)
        engine.snapshot.contracts.put(contract)

        # now call the destroy function
        engine.invoke_syscall_by_name("System.Contract.Destroy")
        # and assert nothing changed to our contracts storage
        self.assertIsNotNone(
            engine.snapshot.contracts.try_get(contract.script_hash()))
コード例 #4
0
ファイル: contract.py プロジェクト: simplitech/neo-mamba
def contract_update(engine: contracts.ApplicationEngine, script: bytes, manifest: bytes) -> None:
    script_len = len(script)
    manifest_len = len(manifest)

    # TODO: In preview 4 revert back to
    # engine.add_gas(engine.STORAGE_PRICE * (script_len + manifest_len))
    # They made a mistake in their storage price calculation logic where manifest size is never taken into account
    engine.add_gas(engine.STORAGE_PRICE * script_len)

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

    if script_len == 0 or script_len > engine.MAX_CONTRACT_LENGTH:
        raise ValueError(f"Invalid script length: {script_len}")

    hash_ = to_script_hash(script)
    if hash_ == engine.current_scripthash or engine.snapshot.contracts.try_get(hash_) is not None:
        raise ValueError("Nothing to update")

    old_contract_has_storage = contract.has_storage
    contract = storage.ContractState(script, contract.manifest)
    contract.manifest.abi.contract_hash = hash_

    engine.snapshot.contracts.put(contract)

    # migrate storage to new contract hash
    with blockchain.Blockchain().backend.get_snapshotview() as snapshot:
        if old_contract_has_storage:
            for key, value in snapshot.storages.find(engine.current_scripthash, b''):
                # delete the old storage
                snapshot.storages.delete(key)
                # update key to new contract hash
                key.contract = contract.script_hash()
                # now persist all data under new contract key
                snapshot.storages.put(key, value)
        snapshot.commit()
    engine.snapshot.contracts.delete(engine.current_scripthash)

    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.script_hash()):
        raise ValueError("Error: manifest does not match with script")
    if (not contract.has_storage
            and len(list(engine.snapshot.storages.find(contract.script_hash(), key_prefix=b''))) != 0):
        raise ValueError("Error: New contract does not support storage while old contract has existing storage")
コード例 #5
0
    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])
コード例 #6
0
    def test_contract_is_standard_fail2(self):
        # can find contract, but is not a signature contract
        engine = test_engine(has_snapshot=True)

        # create a non-standard contract
        script = b'\x01\x02\x03'
        script_hash = to_script_hash(script)
        manifest = contracts.ContractManifest(script_hash)
        contract = storage.ContractState(script, manifest)
        engine.snapshot.contracts.put(contract)

        # push function argument and call
        engine.push(vm.ByteStringStackItem(script_hash.to_array()))
        engine.invoke_syscall_by_name("System.Contract.IsStandard")
        engine.execute()
        self.assertEqual(False, engine.result_stack.pop().to_boolean())
コード例 #7
0
    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")
コード例 #8
0
    def test_contract_update_exceptions4(self):
        # asking to update with a new script but with an invalid (0 length) manifest
        engine = test_engine(has_snapshot=True, default_script=False)

        contract_old = storage.ContractState(hello_world_nef.script,
                                             hello_world_manifest)
        engine.snapshot.contracts.put(contract_old)

        # we load the stored as script to properly setup "engine.current_scripthash"
        engine.load_script(vm.Script(contract_old.script))
        # next we push the necessary items on the stack before calling the update function
        bad_manifest = b''
        engine.push(vm.ByteStringStackItem(bad_manifest))
        engine.push(vm.ByteStringStackItem(bye_world_nef.script))

        with self.assertRaises(ValueError) as context:
            engine.invoke_syscall_by_name("System.Contract.Update")
        self.assertEqual("Invalid manifest length: 0", str(context.exception))
コード例 #9
0
    def test_contract_destroy_ok(self):
        engine = test_engine(has_snapshot=True, default_script=False)
        # for this test we modify our contract to also have storage, to validate it gets cleared properly
        contract = storage.ContractState(hello_world_nef.script,
                                         deepcopy(hello_world_manifest))
        contract.manifest.features |= contracts.ContractFeatures.HAS_STORAGE
        engine.snapshot.contracts.put(contract)

        storage_key = storage.StorageKey(contract.script_hash(), b'firstkey')
        storage_item = storage.StorageItem(b'firstitem')
        engine.snapshot.storages.put(storage_key, storage_item)

        # setup the engine by loading the contract script such that we can call destroy on _that_ contract
        engine.load_script(vm.Script(contract.script))
        engine.invoke_syscall_by_name("System.Contract.Destroy")
        self.assertIsNone(
            engine.snapshot.contracts.try_get(contract.script_hash()))
        self.assertIsNone(engine.snapshot.storages.try_get(storage_key))
コード例 #10
0
    def test_transfer_full_balance(self):
        gas = contracts.GasToken()

        manifest = contracts.ContractManifest()
        manifest.features = contracts.ContractFeatures.PAYABLE
        state_to = storage.ContractState(b'\x00' * 20, manifest)
        account_to = state_to.script_hash()

        account_from = types.UInt160(b'\x01' * 20)
        storage_key_from = storage.StorageKey(
            gas.script_hash, gas._PREFIX_ACCOUNT + account_from.to_array())
        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 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)

        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.script_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))
コード例 #11
0
    def test_contract_update_exceptions3(self):
        # asking to update without making changes to the script
        engine = test_engine(has_snapshot=True, default_script=False)

        contract = storage.ContractState(hello_world_nef.script,
                                         hello_world_manifest)
        engine.snapshot.contracts.put(contract)

        # we load the stored as script to properly setup "engine.current_scripthash"
        engine.load_script(vm.Script(contract.script))
        # next we push the necessary items on the stack before calling the update function
        fake_manifest = b''
        engine.push(vm.ByteStringStackItem(fake_manifest))
        # same script as already exists in storage
        engine.push(vm.ByteStringStackItem(contract.script))

        with self.assertRaises(ValueError) as context:
            engine.invoke_syscall_by_name("System.Contract.Update")
        self.assertEqual("Nothing to update", str(context.exception))
コード例 #12
0
    def test_get_context_no_storage(self):
        engine = test_engine(has_snapshot=True)
        manifest_without_storage = contracts.ContractManifest()
        contract = storage.ContractState(script=self.RET,
                                         _manifest=manifest_without_storage)
        engine.snapshot.contracts.put(contract)

        # first test for asking for a context for a smart contract without storage
        with self.assertRaises(ValueError) as context:
            engine.invoke_syscall_by_name("System.Storage.GetContext")
        self.assertEqual(
            "Cannot get context for smart contract without storage",
            str(context.exception))

        # the same should hold for getting a read only context
        with self.assertRaises(ValueError) as context:
            engine.invoke_syscall_by_name("System.Storage.GetReadOnlyContext")
        self.assertEqual(
            "Cannot get context for smart contract without storage",
            str(context.exception))
コード例 #13
0
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_syscall(syscall_name_to_int("Neo.Native.Deploy"))

    # now call the actual native contract
    sb.emit_contract_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
    engine.snapshot.contracts.put(
        storage.ContractState(sb.to_array(), contracts.ContractManifest()))

    return engine
コード例 #14
0
ファイル: contract.py プロジェクト: simplitech/neo-mamba
def contract_create(engine: contracts.ApplicationEngine, script: bytes, manifest: bytes) -> storage.ContractState:
    script_len = len(script)
    manifest_len = len(manifest)
    if (script_len == 0
            or script_len > engine.MAX_CONTRACT_LENGTH
            or manifest_len == 0
            or manifest_len > contracts.ContractManifest.MAX_LENGTH):
        raise ValueError("Invalid script or manifest length")

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

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

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

    engine.snapshot.contracts.put(contract)
    return contract
コード例 #15
0
    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()))

        contract = storage.ContractState(script=script.to_array(),
                                         _manifest=self.manifest)
        engine.snapshot.contracts.put(contract)

        storage_key = storage.StorageKey(contract.script_hash(), 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())
コード例 #16
0
    def test_transfer_more_than_balance(self):
        gas = contracts.GasToken()
        account_from = types.UInt160.zero()
        storage_key_from = storage.StorageKey(
            gas.script_hash, gas._PREFIX_ACCOUNT + account_from.to_array())
        account_state = gas._state()
        account_state.balance = vm.BigInteger(123)
        storage_item_from = storage.StorageItem(account_state.to_array())

        manifest = contracts.ContractManifest()
        manifest.features = contracts.ContractFeatures.PAYABLE
        state_to = storage.ContractState(b'\x00', manifest)
        account_to = state_to.script_hash()
        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()
        self.assertEqual(1, len(engine.result_stack))
        result = engine.result_stack.pop()
        self.assertFalse(result)
コード例 #17
0
 def setUp(self) -> None:
     self.RET = b'\x40'
     self.manifest = contracts.ContractManifest()
     self.manifest.features = contracts.ContractFeatures.HAS_STORAGE
     self.contract = storage.ContractState(script=self.RET,
                                           _manifest=self.manifest)
コード例 #18
0
    def test_contract_call_exceptions(self):
        engine = test_engine(has_snapshot=True, default_script=False)
        engine.load_script(vm.Script(hello_world_nef.script))
        with self.assertRaises(ValueError) as context:
            contract_call_internal(engine, types.UInt160.zero(),
                                   "_invalid_method",
                                   vm.ArrayStackItem(engine.reference_counter),
                                   contracts.native.CallFlags)
        self.assertEqual(
            "[System.Contract.Call] Method not allowed to start with _",
            str(context.exception))

        # can't find contract
        with self.assertRaises(ValueError) as context:
            contract_call_internal(engine, types.UInt160.zero(),
                                   "valid_method",
                                   vm.ArrayStackItem(engine.reference_counter),
                                   contracts.native.CallFlags)
        self.assertEqual("[System.Contract.Call] Can't find target contract",
                         str(context.exception))

        target_contract = storage.ContractState(contract3_nef.script,
                                                contract3_manifest)
        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
            )
        ]
        new_current_contract = storage.ContractState(hello_world_nef.script,
                                                     new_current_manifest)
        engine.snapshot.contracts.put(new_current_contract)
        with self.assertRaises(ValueError) as context:
            contract_call_internal(engine, target_contract.script_hash(),
                                   "invalid_method",
                                   vm.ArrayStackItem(engine.reference_counter),
                                   contracts.native.CallFlags)
        self.assertEqual(
            "[System.Contract.Call] Not allowed to call target method 'invalid_method' according to manifest",
            str(context.exception))

        # restore current contract to its original form and try to call a non-existing contract
        current_contract = storage.ContractState(hello_world_nef.script,
                                                 hello_world_manifest)
        engine.snapshot.contracts.delete(new_current_contract.script_hash())
        engine.snapshot.contracts.put(current_contract)

        with self.assertRaises(ValueError) as context:
            contract_call_internal(engine, target_contract.script_hash(),
                                   "invalid_method",
                                   vm.ArrayStackItem(engine.reference_counter),
                                   contracts.native.CallFlags)
        self.assertEqual(
            "[System.Contract.Call] requested target method 'invalid_method' 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:
            contract_call_internal(engine, target_contract.script_hash(),
                                   "test_func", array,
                                   contracts.native.CallFlags)
        self.assertEqual(
            "[System.Contract.Call] Invalid number of contract arguments. Expected 0 actual 2",
            str(context.exception))
コード例 #19
0
 def test_contractstate_clone(self):
     manifest = contracts.ContractManifest()
     state = storage.ContractState(b'\x01', manifest)
     clone = state.clone()
     self.assertNotEqual(id(state), id(clone))
     self.assertNotEqual(id(state.manifest), id(clone.manifest))
コード例 #20
0
    def test_transfer_partial_balance_to_account_with_balance(self):
        gas = contracts.GasToken()

        manifest = contracts.ContractManifest()
        manifest.features = contracts.ContractFeatures.PAYABLE
        state_to = storage.ContractState(b'\x00' * 20, manifest)
        account_to = state_to.script_hash()
        storage_key_to = storage.StorageKey(
            gas.script_hash, gas._PREFIX_ACCOUNT + account_to.to_array())
        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 = storage.StorageKey(
            gas.script_hash, gas._PREFIX_ACCOUNT + account_from.to_array())
        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 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)

        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.script_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)
コード例 #21
0
ファイル: test_runtime.py プロジェクト: simplitech/neo-mamba
    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

        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_manifest = contracts.ContractManifest(
            contract_hash=to_script_hash(callee_contract_script))
        callee_manifest.abi.methods = [
            contracts.ContractMethodDescriptor(
                "test_func", 0, [], contracts.ContractParameterType.ANY)
        ]
        callee_contract = storage.ContractState(callee_contract_script,
                                                callee_manifest)

        sb = vm.ScriptBuilder()
        sb.emit(vm.OpCode.NEWARRAY0)  # args (empty array)
        sb.emit_push('test_func')  # target method name
        sb.emit_push(callee_contract.script_hash().to_array())  # contract hash
        sb.emit_syscall(syscall_name_to_int("System.Contract.Call"))
        caller_script = sb.to_array()

        caller_manifest = contracts.ContractManifest(
            contract_hash=to_script_hash(caller_script))
        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 = storage.ContractState(caller_script, caller_manifest)
        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())