def test_getscriptcontainer(self): # first test against an invalid script container (IVerifiable, but not IOperable) engine = test_engine() container = payloads.Header._serializable_init() engine.script_container = container with self.assertRaises(ValueError) as context: engine.invoke_syscall_by_name("System.Runtime.GetScriptContainer") self.assertEqual("script container is not a valid IInteroperable type", str(context.exception)) tx = test_tx(1) engine = test_engine() engine.script_container = tx sb = vm.ScriptBuilder() sb.emit_syscall( syscall_name_to_int("System.Runtime.GetScriptContainer")) engine.load_script(vm.Script(sb.to_array())) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack._items)) item = engine.result_stack.pop() self.assertIsInstance(item, vm.ArrayStackItem) # we now have a Block that has been serialized, let's check the hash self.assertEqual(vm.ByteStringStackItem(tx.hash().to_array()), item[0])
def test_gasleft(self): engine = test_engine() engine.is_test_mode = True sb = vm.ScriptBuilder() sb.emit_syscall(syscall_name_to_int("System.Runtime.GasLeft")) data = sb.to_array() # test with test mode engine.load_script(vm.Script(data)) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack._items)) item = engine.result_stack.pop() self.assertEqual(vm.IntegerStackItem(-1), item) # test with actual consumption engine = test_engine() engine.is_test_mode = False engine.gas_amount = 500 # we can re-use the script engine.load_script(vm.Script(data)) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack._items)) item = engine.result_stack.pop() # the syscall itself costs 400 self.assertEqual(vm.IntegerStackItem(100), 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_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)) caller_contract = storage.ContractState(caller_script, caller_manifest) 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 invoke_syscall_by_name(self, method: str) -> Any: """ Helper function to call `on_syscall` using the syscall name. Args: method: full qualified syscall name. e.g. "System.Runtime.Platform" Returns: the result of the syscall handler. e.g. for "System.Runtime.Platform" returns "NEO" """ return self.on_syscall(contracts.syscall_name_to_int(method))
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_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) # create caller_contract script 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)) caller_contract = storage.ContractState(caller_script, caller_manifest) 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(caller_contract.script_hash().to_array(), item.to_array())
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_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)) caller_contract = storage.ContractState(caller_script, caller_manifest) 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_is_signature_contract(self): """ A valid signature contract script looks as follows - PUSHDATA1 (0xC) - LEN PUBLIC KEY (33) - PUBLIC KEY data - PUSHNULL (0xB) - SYSCALL (0x41) - "Neo.Crypto.VerifyWithECDsaSecp256r1" identifier """ incorrect_script_len = b'\x01' * 10 self.assertFalse( contracts.Contract.is_signature_contract(incorrect_script_len)) # first byte should be PUSHDATA1 (0xC) incorrect_script_start_byte = b'\x01' * 41 self.assertFalse( contracts.Contract.is_signature_contract( incorrect_script_start_byte)) # second byte should be 33 incorrect_second_byte = bytearray(b'\x01' * 41) incorrect_second_byte[0] = int(vm.OpCode.PUSHDATA1) self.assertFalse( contracts.Contract.is_signature_contract(incorrect_second_byte)) # index 35 should be PUSHNULL incorrect_idx_35 = bytearray([0xc, 33]) + b'\01' * 39 self.assertFalse( contracts.Contract.is_signature_contract(incorrect_idx_35)) # index 36 should be SYSCALL incorrect_idx_36 = bytearray([0xc, 33]) + b'\01' * 39 incorrect_idx_36[35] = int(vm.OpCode.PUSHNULL) self.assertFalse( contracts.Contract.is_signature_contract( incorrect_idx_36)) # index 36 should be SYSCALL # the last 4 bytes should be the "Neo.Crypto.VerifyWithECDsaSecp256r1" SYSCALL incorrect_syscall_number = bytearray([0xc, 33]) + b'\01' * 39 incorrect_syscall_number[35] = int(vm.OpCode.PUSHNULL) incorrect_syscall_number[36] = int(vm.OpCode.SYSCALL) self.assertFalse( contracts.Contract.is_signature_contract(incorrect_syscall_number)) # and finally a contract that matches the correct format correct = bytearray([0xc, 33]) + b'\01' * 39 correct[35] = int(vm.OpCode.PUSHNULL) correct[36] = int(vm.OpCode.SYSCALL) correct[37:41] = contracts.syscall_name_to_int( "Neo.Crypto.VerifyWithECDsaSecp256r1").to_bytes(4, 'little') self.assertTrue(contracts.Contract.is_signature_contract(correct))
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_get_invocation_counter_fail(self): # current script has no invocation calls 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")) engine.load_script(vm.Script(sb.to_array())) engine.execute() self.assertEqual(vm.VMState.FAULT, engine.state) self.assertIn( "Failed to get invocation counter for the current context", engine.exception_message)
def create_signature_redeemscript( public_key: cryptography.ECPoint) -> bytes: """ Create a single signature redeem script. This generated script is intended to be executed by the VM to indicate that the requested action is allowed. Args: public_key: the public key to use during verification. """ sb = vm.ScriptBuilder() sb.emit_push(public_key.encode_point(True)) sb.emit_syscall( contracts.syscall_name_to_int("System.Crypto.CheckSig")) return sb.to_array()
def is_signature_contract(script: bytes) -> bool: """ Test if the provided script is signature contract. Args: script: contract script. """ if len(script) != 40: return False if (script[0] != vm.OpCode.PUSHDATA1 or script[1] != 33 or script[35] != vm.OpCode.SYSCALL or int.from_bytes(script[36:40], 'little') != contracts.syscall_name_to_int("System.Crypto.CheckSig")): return False return True
def test_getexecutingscripthash(self): engine = test_engine() sb = vm.ScriptBuilder() sb.emit(vm.OpCode.PUSH1) sb.emit(vm.OpCode.PUSH2) sb.emit(vm.OpCode.PUSH3) sb.emit_syscall( syscall_name_to_int("System.Runtime.GetExecutingScriptHash")) data = sb.to_array() engine.load_script(vm.Script(data)) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(4, len(engine.result_stack._items)) item = engine.result_stack.pop() self.assertEqual(to_script_hash(data).to_array(), item.to_array())
def test_getentryscripthash(self): # entry script hash is set on a engine.load_script sb = vm.ScriptBuilder() sb.emit_syscall( syscall_name_to_int("System.Runtime.GetEntryScriptHash")) raw_script = sb.to_array() engine = test_engine(default_script=False) ctx = engine.load_script(vm.Script(raw_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(raw_script).to_array(), item.to_array())
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 create_multisig_redeemscript( m: int, public_keys: List[cryptography.ECPoint]) -> bytes: """ Create a multi-signature redeem script requiring `m` signatures from the list `public_keys`. This generated script is intended to be executed by the VM to indicate that the requested action is allowed. Args: m: minimum number of signature required for signing. Can't be lower than 2. public_keys: public keys to use during verification. Raises: ValueError: if the minimum required signatures is not met. ValueError: if the maximum allowed signatures is exceeded. ValueError: if the maximum allowed public keys is exceeded. """ if m < 1: raise ValueError( f"Minimum required signature count is 1, specified {m}.") if m > len(public_keys): raise ValueError( "Invalid public key count. " "Minimum required signatures is bigger than supplied public keys count." ) if len(public_keys) > 1024: raise ValueError( f"Supplied public key count ({len(public_keys)}) exceeds maximum of 1024." ) sb = vm.ScriptBuilder() sb.emit_push(m) public_keys.sort() for key in public_keys: sb.emit_push(key.encode_point(True)) sb.emit_push(len(public_keys)) sb.emit(vm.OpCode.PUSHNULL) sb.emit_syscall( contracts.syscall_name_to_int( "Neo.Crypto.CheckMultisigWithECDsaSecp256r1")) return sb.to_array()
def test_is_multisig_contract_256_pubkeys(self): # test handling of a large number of public keys # from 256 signatures and above he count is encoded in 2 bytes # for this test we manually encoded a length of 2 into 2 bytes script = bytearray([int(vm.OpCode.PUSHINT16), 2, 0]) # add fake public keys for _ in range(0, 2): script += bytearray([int(vm.OpCode.PUSHDATA1)]) script += bytearray([33]) script += b'\xDD' * 33 # and now mismatch the public key count value we say is present (0 here) script += bytearray([int(vm.OpCode.PUSHINT16), 0, 0]) self.assertFalse(contracts.Contract.is_multisig_contract(script)) # now we correct the public key count in the script and make it valid by adding the expected tail script[-2] = 2 script += bytearray([int(vm.OpCode.SYSCALL)]) script += contracts.syscall_name_to_int("System.Crypto.CheckMultisig").to_bytes(4, 'little') self.assertTrue(contracts.Contract.is_multisig_contract(script))
def test_gasleft(self): engine = test_engine() engine.is_test_mode = True sb = vm.ScriptBuilder() sb.emit_syscall(syscall_name_to_int("System.Runtime.GasLeft")) data = sb.to_array() # test with test mode engine.load_script(vm.Script(data)) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack._items)) item = engine.result_stack.pop() self.assertEqual(vm.IntegerStackItem(-1), item) """ using (var engine = ApplicationEngine.Create(TriggerType.Application, null, null, gas:500)) using (var script = new ScriptBuilder()) { script.EmitSysCall(ApplicationEngine.System_Runtime_GasLeft); engine.LoadScript(script.ToArray()); Assert.AreEqual(VMState.HALT, engine.Execute()); Assert.AreEqual(engine.ResultStack.Count, 1); Console.WriteLine(engine.ResultStack.Pop().GetInteger()); } """ # test with actual consumption engine = test_engine() engine.is_test_mode = False engine.gas_amount = 500 # we can re-use the script engine.load_script(vm.Script(data)) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack._items)) item = engine.result_stack.pop() self.assertEqual(vm.IntegerStackItem(20), item)
def _create_genesis_block() -> payloads.Block: script = vm.ScriptBuilder().emit_syscall( contracts.syscall_name_to_int("Neo.Native.Deploy")).to_array() b = payloads.Block( version=0, prev_hash=types.UInt256.zero(), timestamp=int( datetime(2016, 7, 15, 15, 8, 21, 0, timezone.utc).timestamp() * 1000), index=0, next_consensus=Blockchain.get_consensus_address( settings.standby_validators), witness=payloads.Witness( invocation_script=b'', verification_script=b'\x11' # (OpCode.PUSH1) ), consensus_data=payloads.ConsensusData(primary_index=0, nonce=2083236893), transactions=[ payloads.Transaction( version=0, script=script, system_fee=0, network_fee=0, nonce=0, valid_until_block=0, signers=[ payloads.Signer(account=to_script_hash(b'\x11'), scope=payloads.WitnessScope.FEE_ONLY) ], witnesses=[ payloads.Witness(invocation_script=b'', verification_script=b'\x11') ]) ]) return b
def test_contract_create_ok(self): engine = test_engine(has_snapshot=True) sb = vm.ScriptBuilder() sb.emit_push(str(hello_world_manifest).encode()) sb.emit_push(hello_world_nef.script) sb.emit_syscall(syscall_name_to_int("System.Contract.Create")) engine.load_script(vm.Script(sb.to_array())) engine.execute() self.assertEqual(vm.VMState.HALT, engine.state) self.assertEqual(1, len(engine.result_stack._items)) item = engine.result_stack.pop() # returns a serialized contract state self.assertEqual(hello_world_nef.script, item[0].to_array()) self.assertEqual( hello_world_manifest, contracts.ContractManifest.from_json(json.loads( item[1].to_array()))) self.assertEqual( contracts.ContractFeatures.HAS_STORAGE in hello_world_manifest.features, item[2]) self.assertEqual( contracts.ContractFeatures.PAYABLE in hello_world_manifest.features, item[3]) return engine
def parse_as_multisig_contract( script: bytes) -> Tuple[bool, int, List[cryptography.ECPoint]]: """ Try to parse script as multisig contract and extract related data. Args: script: array of vm byte code Returns: bool: True if the script passes as a valid multisignature contract script. False otherwise int: the signing threshold if validation passed. 0 otherwise list[ECPoint]: the public keys in the script if valiation passed. An empty array otherwise. """ script = bytes(script) VALIDATION_FAILURE: Tuple[bool, int, List[cryptography.ECPoint]] = (False, 0, []) len_script = len(script) if len_script < 42: return VALIDATION_FAILURE # read signature length, which is encoded as variable_length first_byte = script[0] if first_byte == int(vm.OpCode.PUSHINT8): signature_threshold = script[1] i = 2 elif first_byte == int(vm.OpCode.PUSHINT16): signature_threshold = int.from_bytes(script[1:3], 'little', signed=False) i = 3 elif int(vm.OpCode.PUSH1) <= first_byte <= int(vm.OpCode.PUSH16): signature_threshold = first_byte - int(vm.OpCode.PUSH0) i = 1 else: return VALIDATION_FAILURE if signature_threshold < 1 or signature_threshold > 1024: return VALIDATION_FAILURE # try reading public keys and do a basic format validation pushdata1 = int(vm.OpCode.PUSHDATA1) public_keys = [] while script[i] == pushdata1: if len_script <= i + 35: return VALIDATION_FAILURE if script[i + 1] != 33: return VALIDATION_FAILURE public_keys.append( cryptography.ECPoint.deserialize_from_bytes(script[i + 2:i + 2 + 33])) i += 35 public_key_count = len(public_keys) if public_key_count < signature_threshold or public_key_count > 1024: return VALIDATION_FAILURE # validate that the number of collected public keys match the expected count value = script[i] if value == int(vm.OpCode.PUSHINT8): if len_script <= i + 1 or public_key_count != script[i + 1]: return VALIDATION_FAILURE i += 2 elif value == int(vm.OpCode.PUSHINT16): if len_script < i + 3 or public_key_count != int.from_bytes( script[i + 1:i + 3], 'little', signed=False): return VALIDATION_FAILURE i += 3 elif int(vm.OpCode.PUSH1) <= value <= int(vm.OpCode.PUSH16): if public_key_count != value - int(vm.OpCode.PUSH0): return VALIDATION_FAILURE i += 1 else: return VALIDATION_FAILURE if len_script != i + 5: return VALIDATION_FAILURE if script[i] != int(vm.OpCode.SYSCALL): return VALIDATION_FAILURE i += 1 syscall_num = int.from_bytes(script[i:i + 4], 'little') if syscall_num != contracts.syscall_name_to_int( "System.Crypto.CheckMultisig"): return VALIDATION_FAILURE return True, signature_threshold, public_keys
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())
def is_multisig_contract(script: bytes) -> bool: """ Test if the provided script is multi-signature contract. Args: script: contract script. """ len_script = len(script) if len_script < 43: return False # read signature length, which is encoded as variable_length first_byte = script[0] if first_byte == int(vm.OpCode.PUSHINT8): signature_count = script[1] i = 2 elif first_byte == int(vm.OpCode.PUSHINT16): signature_count = int.from_bytes(script[1:3], 'little', signed=False) i = 3 elif int(vm.OpCode.PUSH1) <= first_byte <= int(vm.OpCode.PUSH16): signature_count = first_byte - int(vm.OpCode.PUSH0) i = 1 else: return False if signature_count < 1 or signature_count > 1024: return False # try reading public keys and do a basic format validation pushdata1 = int(vm.OpCode.PUSHDATA1) public_key_count = 0 while script[i] == pushdata1: if len_script <= i + 35: return False if script[i + 1] != 33: return False i += 35 public_key_count += 1 if public_key_count < signature_count or public_key_count > 1024: return False # validate that the number of collected public keys match the expected count value = script[i] if value == int(vm.OpCode.PUSHINT8): if len_script <= i + 1 or public_key_count != script[i + 1]: return False i += 2 elif value == int(vm.OpCode.PUSHINT16): if len_script < i + 3 or public_key_count != int.from_bytes( script[i + 1:i + 3], 'little', signed=False): return False i += 3 elif int(vm.OpCode.PUSH1) <= value <= int(vm.OpCode.PUSH16): if public_key_count != value - int(vm.OpCode.PUSH0): return False i += 1 else: return False if len_script != i + 6: return False if script[i] != int(vm.OpCode.PUSHNULL): return False if script[i + 1] != int(vm.OpCode.SYSCALL): return False i += 2 syscall_num = int.from_bytes(script[i:i + 4], 'little') if syscall_num != contracts.syscall_name_to_int( "Neo.Crypto.CheckMultisigWithECDsaSecp256r1"): return False return True
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())