def test_2_serialize_notify_no_payload(self): sc = SmartContractEvent(SmartContractEvent.RUNTIME_NOTIFY, [], self.contract_hash, 99, self.event_tx, True, False) stream = StreamManager.GetStream() writer = BinaryWriter(stream) sc.Serialize(writer) out = bytes(stream.getvalue()) self.assertEqual( out, b'\x1cSmartContract.Runtime.Notify\x11\xc4\xd1\xf4\xfb\xa6\x19\xf2b\x88p\xd3n:\x97s\xe8tp[c\x00\x00\x00\x90\xe4\xf1\xbbb\x8e\xf1\x07\xde\xe9\xf0\xd2\x12\xd1w\xbco\x844\x07=\x1b\xa7\x1f\xa7\x94`\x0b\xb4\x88|K' ) StreamManager.ReleaseStream(stream) new_event = SmartContractEvent.FromByteArray(out) self.assertEqual(new_event.event_type, sc.event_type) self.assertEqual(new_event.contract_hash, sc.contract_hash) self.assertEqual(new_event.test_mode, sc.test_mode) self.assertEqual(new_event.tx_hash, sc.tx_hash) self.assertEqual(new_event.block_number, sc.block_number)
def test_1_serialize_runtime_log(self): sc = SmartContractEvent( SmartContractEvent.RUNTIME_LOG, ContractParameter(ContractParameterType.Array, []), self.contract_hash, 99999, self.event_tx, True, False) stream = StreamManager.GetStream() writer = BinaryWriter(stream) sc.Serialize(writer) out = bytes(stream.getvalue()) self.assertEqual( out, b'\x19SmartContract.Runtime.Log\x11\xc4\xd1\xf4\xfb\xa6\x19\xf2b\x88p\xd3n:\x97s\xe8tp[\x9f\x86\x01\x00\x90\xe4\xf1\xbbb\x8e\xf1\x07\xde\xe9\xf0\xd2\x12\xd1w\xbco\x844\x07=\x1b\xa7\x1f\xa7\x94`\x0b\xb4\x88|K' ) StreamManager.ReleaseStream(stream) new_event = SmartContractEvent.FromByteArray(out) self.assertEqual(new_event.event_type, sc.event_type) self.assertEqual(new_event.contract_hash, sc.contract_hash) self.assertEqual(new_event.test_mode, sc.test_mode) self.assertEqual(new_event.tx_hash, sc.tx_hash) self.assertEqual(new_event.block_number, sc.block_number)
def ExecutionCompleted(self, engine, success, error=None): height = Blockchain.Default().Height tx_hash = engine.ScriptContainer.Hash entry_script = None try: # get the first script that was executed # this is usually the script that sets up the script to be executed entry_script = UInt160(data=engine.ExecutedScriptHashes[0]) # ExecutedScriptHashes[1] will usually be the first contract executed if len(engine.ExecutedScriptHashes) > 1: entry_script = UInt160(data=engine.ExecutedScriptHashes[1]) except Exception as e: logger.error("Could not get entry script: %s " % e) payload = [] for item in engine.EvaluationStack.Items: payload_item = stack_item_to_py(item) payload.append(payload_item) if success: # dispatch all notify events, along with the success of the contract execution for notify_event_args in self.notifications: self.events_to_dispatch.append( NotifyEvent(SmartContractEvent.RUNTIME_NOTIFY, notify_event_args.State, notify_event_args.ScriptHash, height, tx_hash, success, engine.testMode)) if engine.Trigger == Application: self.events_to_dispatch.append( SmartContractEvent(SmartContractEvent.EXECUTION_SUCCESS, payload, entry_script, height, tx_hash, success, engine.testMode)) else: self.events_to_dispatch.append( SmartContractEvent(SmartContractEvent.VERIFICATION_SUCCESS, payload, entry_script, height, tx_hash, success, engine.testMode)) else: if engine.Trigger == Application: self.events_to_dispatch.append( SmartContractEvent(SmartContractEvent.EXECUTION_FAIL, [payload, error, engine._VMState], entry_script, height, tx_hash, success, engine.testMode)) else: self.events_to_dispatch.append( SmartContractEvent(SmartContractEvent.VERIFICATION_FAIL, [payload, error, engine._VMState], entry_script, height, tx_hash, success, engine.testMode)) self.notifications = []
def test_4_serialize_full_transfer_notify_payload(self): payload = ContractParameter(ContractParameterType.Array, [ ContractParameter(ContractParameterType.String, b'transfer'), ContractParameter(ContractParameterType.ByteArray, self.addr_to), ContractParameter(ContractParameterType.ByteArray, self.addr_from), ContractParameter(ContractParameterType.Integer, 123000) ]) sc = NotifyEvent(SmartContractEvent.RUNTIME_NOTIFY, payload, self.contract_hash, 91349, self.event_tx, True, False) stream = StreamManager.GetStream() writer = BinaryWriter(stream) sc.Serialize(writer) out = bytes(stream.getvalue()) StreamManager.ReleaseStream(stream) new_event = SmartContractEvent.FromByteArray(out) self.assertEqual(new_event.event_type, sc.event_type) self.assertEqual(new_event.contract_hash, sc.contract_hash) self.assertEqual(new_event.test_mode, sc.test_mode) self.assertEqual(new_event.tx_hash, sc.tx_hash) self.assertEqual(new_event.block_number, sc.block_number) self.assertEqual(new_event.notify_type, b'transfer') self.assertEqual(new_event.AddressTo, 'ALb8FEhEmtSqv97fuNVuoLmcmrSKckffRf') self.assertEqual(new_event.AddressFrom, 'AKZmSGPD7ytJBbxpRPmobYGLNxdWH3Jiqs') self.assertEqual(new_event.Amount, 123000) self.assertEqual(new_event.is_standard_notify, True)
def test_2_serialize_single_notify_payload(self): sc = NotifyEvent(SmartContractEvent.RUNTIME_NOTIFY, ContractParameter(ContractParameterType.Array, [ContractParameter(ContractParameterType.String, b'hello')]), self.contract_hash, 99, self.event_tx, True, False) stream = StreamManager.GetStream() writer = BinaryWriter(stream) sc.Serialize(writer) out = bytes(stream.getvalue()) self.assertEqual(out, b'\x1cSmartContract.Runtime.Notify\x11\xc4\xd1\xf4\xfb\xa6\x19\xf2b\x88p\xd3n:\x97s\xe8tp[c\x00\x00\x00\x90\xe4\xf1\xbbb\x8e\xf1\x07\xde\xe9\xf0\xd2\x12\xd1w\xbco\x844\x07=\x1b\xa7\x1f\xa7\x94`\x0b\xb4\x88|K\x05hello') StreamManager.ReleaseStream(stream) new_event = SmartContractEvent.FromByteArray(out) self.assertEqual(new_event.event_type, sc.event_type) self.assertEqual(new_event.contract_hash, sc.contract_hash) self.assertEqual(new_event.test_mode, sc.test_mode) self.assertEqual(new_event.tx_hash, sc.tx_hash) self.assertEqual(new_event.block_number, sc.block_number) self.assertEqual(new_event.notify_type, b'hello') self.assertEqual(new_event.AddressFrom, None) self.assertEqual(new_event.AddressTo, None) self.assertEqual(new_event.Amount, 0) self.assertEqual(new_event.is_standard_notify, False)
def Contract_Destroy(self, engine): hash = UInt160(data=engine.CurrentContext.ScriptHash()) contract = self.Snapshot.Contracts.TryGet(hash.ToBytes()) if contract is not None: self.Snapshot.Contracts.Delete(hash.ToBytes()) if contract.HasStorage: to_del = [] for k, v in self.Snapshot.Storages.Find(hash.ToArray()): storage_key = StorageKey(script_hash=hash, key=k[20:]) # Snapshot.Storages.Delete() modifies the underlying dictionary of the cache we'd be iterating # over using Storages.Find. We therefore need to postpone deletion to_del.append(storage_key) for storage_key in to_del: self.Snapshot.Storages.Delete(storage_key.ToArray()) self.events_to_dispatch.append( SmartContractEvent(SmartContractEvent.CONTRACT_DESTROY, ContractParameter( ContractParameterType.InteropInterface, contract), hash, GetBlockchain().Height + 1, engine.ScriptContainer.Hash if engine.ScriptContainer else None, test_mode=engine.testMode)) return True
def test_3_serialize_single_transfer_notify_payload(self): sc = NotifyEvent(SmartContractEvent.RUNTIME_NOTIFY, [b'transfer'], self.contract_hash, 99, self.event_tx, True, False) stream = StreamManager.GetStream() writer = BinaryWriter(stream) sc.Serialize(writer) out = bytes(stream.getvalue()) StreamManager.ReleaseStream(stream) new_event = SmartContractEvent.FromByteArray(out) self.assertEqual(new_event.event_type, sc.event_type) self.assertEqual(new_event.contract_hash, sc.contract_hash) self.assertEqual(new_event.test_mode, sc.test_mode) self.assertEqual(new_event.tx_hash, sc.tx_hash) self.assertEqual(new_event.block_number, sc.block_number) self.assertEqual(new_event.notify_type, b'transfer') self.assertEqual(new_event.AddressFrom, None) self.assertEqual(new_event.AddressTo, None) self.assertEqual(new_event.Amount, 0) self.assertEqual(new_event.is_standard_notify, False) self.assertEqual(new_event.ShouldPersist, False)
def get_by_addr(self, address): """ Lookup a set of notifications by address Args: address (UInt160 or str): hash of address for notifications Returns: list: a list of notifications """ addr = address if isinstance(address, str) and len(address) == 34: addr = Helper.AddrStrToScriptHash(address) if not isinstance(addr, UInt160): raise Exception("Incorrect address format") addrlist_snapshot = self.db.prefixed_db(NotificationPrefix.PREFIX_ADDR).snapshot() results = [] for val in addrlist_snapshot.iterator(prefix=bytes(addr.Data), include_key=False): if len(val) > 4: try: event = SmartContractEvent.FromByteArray(val) results.append(event) except Exception as e: logger.error("could not parse event: %s %s" % (e, val)) return results
def test_5_serialize_full_refund_payload(self): sc = NotifyEvent(SmartContractEvent.RUNTIME_NOTIFY, [b'refund', self.addr_to, BigInteger(123000)], self.contract_hash, 91349, self.event_tx, True, False) stream = StreamManager.GetStream() writer = BinaryWriter(stream) sc.Serialize(writer) out = bytes(stream.getvalue()) StreamManager.ReleaseStream(stream) new_event = SmartContractEvent.FromByteArray(out) self.assertEqual(new_event.event_type, sc.event_type) self.assertEqual(new_event.contract_hash, sc.contract_hash) self.assertEqual(new_event.test_mode, sc.test_mode) self.assertEqual(new_event.tx_hash, sc.tx_hash) self.assertEqual(new_event.block_number, sc.block_number) self.assertEqual(new_event.notify_type, b'refund') self.assertEqual(new_event.AddressTo, 'AKZmSGPD7ytJBbxpRPmobYGLNxdWH3Jiqs') self.assertEqual(new_event.addr_from, sc.contract_hash) self.assertEqual(new_event.Amount, 123000) self.assertEqual(new_event.is_standard_notify, True)
def Runtime_Log(self, engine: ExecutionEngine): item = engine.CurrentContext.EvaluationStack.Pop() # will raise an exception for types that don't support it item.GetByteArray() # if we pass we can call the convenience method to pretty print the data message = item.GetString() hash = UInt160(data=engine.CurrentContext.ScriptHash()) tx_hash = None if engine.ScriptContainer: tx_hash = engine.ScriptContainer.Hash engine.write_log(str(message)) # Build and emit smart contract event self.events_to_dispatch.append( SmartContractEvent(SmartContractEvent.RUNTIME_LOG, ContractParameter(ContractParameterType.String, value=message), hash, GetBlockchain().Height + 1, tx_hash, test_mode=engine.testMode)) return True
def get_by_contract(self, contract_hash): """ Look up a set of notifications by the contract they are associated with Args: contract_hash (UInt160 or str): hash of contract for notifications to be retreived Returns: list: a list of notifications """ hash = contract_hash if isinstance(contract_hash, str) and len(contract_hash) == 40: hash = UInt160.ParseString(contract_hash) if not isinstance(hash, UInt160): raise Exception("Incorrect address format") contractlist_snapshot = self.db.prefixed_db(NotificationPrefix.PREFIX_CONTRACT).snapshot() results = [] for val in contractlist_snapshot.iterator(prefix=bytes(hash.Data), include_key=False): if len(val) > 4: try: event = SmartContractEvent.FromByteArray(val) results.append(event) except Exception as e: logger.error("could not parse event: %s %s" % (e, val)) return results
def test_6_serialize_full_approve_payload(self): sc = NotifyEvent( SmartContractEvent.RUNTIME_NOTIFY, [b'approve', self.addr_to, self.addr_from, b'x\xe0\x01'], self.contract_hash, 91349, self.event_tx, True, False) stream = StreamManager.GetStream() writer = BinaryWriter(stream) sc.Serialize(writer) out = bytes(stream.getvalue()) StreamManager.ReleaseStream(stream) new_event = SmartContractEvent.FromByteArray(out) self.assertEqual(new_event.event_type, sc.event_type) self.assertEqual(new_event.contract_hash, sc.contract_hash) self.assertEqual(new_event.test_mode, sc.test_mode) self.assertEqual(new_event.tx_hash, sc.tx_hash) self.assertEqual(new_event.block_number, sc.block_number) self.assertEqual(new_event.notify_type, b'approve') self.assertEqual(new_event.AddressFrom, 'AKZmSGPD7ytJBbxpRPmobYGLNxdWH3Jiqs') self.assertEqual(new_event.AddressTo, 'ALb8FEhEmtSqv97fuNVuoLmcmrSKckffRf') self.assertEqual(new_event.Amount, 123000) self.assertEqual(new_event.is_standard_notify, True) self.assertEqual(new_event.ShouldPersist, True)
def test_2_persist_isnt_notify_event(self): sc = SmartContractEvent(SmartContractEvent.RUNTIME_NOTIFY, ContractParameter(ContractParameterType.Array, []), self.contract_hash, 99, self.event_tx, True, False) ndb = NotificationDB.instance() ndb.on_smart_contract_event(sc) self.assertEqual(ndb.current_events, [])
def Storage_Delete(self, engine: ExecutionEngine): if self.Trigger != TriggerType.Application and self.Trigger != TriggerType.ApplicationR: return False context = engine.CurrentContext.EvaluationStack.Pop().GetInterface( neo.SmartContract.StorageContext.StorageContext) if not self.CheckStorageContext(context): return False if context.IsReadOnly: return False key = engine.CurrentContext.EvaluationStack.Pop().GetByteArray() storage_key = StorageKey(script_hash=context.ScriptHash, key=key) if type(engine) == ExecutionEngine: test_mode = False else: test_mode = engine.testMode self.events_to_dispatch.append( SmartContractEvent(SmartContractEvent.STORAGE_DELETE, ContractParameter(ContractParameterType.String, key), context.ScriptHash, GetBlockchain().Height + 1, engine.ScriptContainer.Hash if engine.ScriptContainer else None, test_mode=test_mode)) item = self.Snapshot.Storages.TryGet(storage_key.ToArray()) if item and item.IsConstant: return False self.Snapshot.Storages.Delete(storage_key.ToArray()) return True
def dispatch_smart_contract_event(event_type, event_payload, contract_hash, block_number, tx_hash, execution_success=False, test_mode=False): sc_event = SmartContractEvent(event_type, event_payload, contract_hash, block_number, tx_hash, execution_success, test_mode) events.emit(event_type, sc_event)
def on_smart_contract_created(self, sc_event: SmartContractEvent): """ Listener for SmartContractEvent Args: sc_event (SmartContractEvent): event to check and see if it contains NEP5Token created """ if isinstance(sc_event.contract, ContractState): sc_event.CheckIsNEP5() if sc_event.token: self._new_contracts_to_write.append(sc_event)
def Storage_Get(self, engine): context = None try: item = engine.EvaluationStack.Pop() context = item.GetInterface() shash = context.ScriptHash except Exception as e: logger.error("could not get storage context %s " % e) return False contract = Blockchain.Default().GetContract( context.ScriptHash.ToBytes()) if contract is not None: if not contract.HasStorage: return False else: return False key = engine.EvaluationStack.Pop().GetByteArray() storage_key = StorageKey(script_hash=context.ScriptHash, key=key) item = Blockchain.Default().GetStorageItem(storage_key) keystr = key valStr = bytearray(0) if item is not None: valStr = bytearray(item.Value) if len(key) == 20: keystr = Crypto.ToAddress(UInt160(data=key)) try: valStr = int.from_bytes(valStr, 'little') except Exception as e: logger.error("Could not convert %s to number: %s " % (valStr, e)) if item is not None: engine.EvaluationStack.PushT(bytearray(item.Value)) else: engine.EvaluationStack.PushT(bytearray(0)) self.events_to_dispatch.append( SmartContractEvent(SmartContractEvent.STORAGE_GET, ['%s -> %s' % (keystr, valStr)], context.ScriptHash, Blockchain.Default().Height + 1, engine.ScriptContainer.Hash, test_mode=engine.testMode)) return True
def Storage_Get(self, engine): context = None try: item = engine.EvaluationStack.Pop() context = item.GetInterface() except Exception as e: logger.error("could not get storage context %s " % e) return False if not self.CheckStorageContext(context): return False key = engine.EvaluationStack.Pop().GetByteArray() storage_key = StorageKey(script_hash=context.ScriptHash, key=key) item = self.Storages.TryGet(storage_key.ToArray()) keystr = key valStr = bytearray(0) if item is not None: valStr = bytearray(item.Value) if len(key) == 20: keystr = Crypto.ToAddress(UInt160(data=key)) try: valStr = int.from_bytes(valStr, 'little') except Exception as e: logger.error("Could not convert %s to number: %s " % (valStr, e)) if item is not None: engine.EvaluationStack.PushT(bytearray(item.Value)) else: engine.EvaluationStack.PushT(bytearray(0)) tx_hash = None if engine.ScriptContainer: tx_hash = engine.ScriptContainer.Hash self.events_to_dispatch.append( SmartContractEvent(SmartContractEvent.STORAGE_GET, ContractParameter(ContractParameterType.String, value='%s -> %s' % (keystr, valStr)), context.ScriptHash, Blockchain.Default().Height + 1, tx_hash, test_mode=engine.testMode)) return True
def get_tokens(self): """ Looks up all tokens Returns: list: A list of smart contract events with contracts that are NEP5 Tokens """ tokens_snapshot = self.db.prefixed_db(NotificationPrefix.PREFIX_TOKEN).snapshot() results = [] for val in tokens_snapshot.iterator(include_key=False): event = SmartContractEvent.FromByteArray(val) results.append(event) return results
def get_by_block(self, block_number): blocklist_snapshot = self.db.prefixed_db( NotificationPrefix.PREFIX_BLOCK).snapshot() block_bytes = block_number.to_bytes(4, 'little') results = [] for val in blocklist_snapshot.iterator(prefix=block_bytes, include_key=False): event = SmartContractEvent.FromByteArray(val) results.append(event) return results
def Runtime_Log(self, engine): message = engine.EvaluationStack.Pop().GetByteArray() hash = UInt160(data=engine.CurrentContext.ScriptHash()) # Build and emit smart contract event self.events_to_dispatch.append( SmartContractEvent(SmartContractEvent.RUNTIME_LOG, [message], hash, Blockchain.Default().Height, engine.ScriptContainer.Hash, test_mode=engine.testMode)) return True
def get_tokens(self): """ Looks up all tokens Returns: list: A list of smart contract events with contracts that are NEP5 Tokens """ tokens_snapshot = self.db.getPrefixedDB( NotificationPrefix.PREFIX_TOKEN).createSnapshot() results = [] with tokens_snapshot.db.openIter( DBProperties(include_key=False)) as it: for val in it: event = SmartContractEvent.FromByteArray(val) results.append(event) return results
def get_by_block(self, block_number): """ Look up notifications for a block Args: block_number (int): height of block to search for notifications Returns: list: a list of notifications """ blocklist_snapshot = self.db.prefixed_db(NotificationPrefix.PREFIX_BLOCK).snapshot() block_bytes = block_number.to_bytes(4, 'little') results = [] for val in blocklist_snapshot.iterator(prefix=block_bytes, include_key=False): event = SmartContractEvent.FromByteArray(val) results.append(event) return results
def Storage_Get(self, engine: ExecutionEngine): context = None try: item = engine.CurrentContext.EvaluationStack.Pop() context = item.GetInterface( neo.SmartContract.StorageContext.StorageContext) except Exception as e: return False if not self.CheckStorageContext(context): return False key = engine.CurrentContext.EvaluationStack.Pop().GetByteArray() storage_key = StorageKey(script_hash=context.ScriptHash, key=key) item = self.Snapshot.Storages.TryGet(storage_key.ToArray()) keystr = key valStr = bytearray(0) if item is not None: valStr = bytearray(item.Value) if item is not None: engine.CurrentContext.EvaluationStack.PushT(bytearray(item.Value)) else: engine.CurrentContext.EvaluationStack.PushT(bytearray(0)) tx_hash = None if engine.ScriptContainer: tx_hash = engine.ScriptContainer.Hash self.events_to_dispatch.append( SmartContractEvent(SmartContractEvent.STORAGE_GET, ContractParameter(ContractParameterType.String, value='%s -> %s' % (keystr, valStr)), context.ScriptHash, GetBlockchain().Height + 1, tx_hash, test_mode=engine.testMode)) return True
def get_token(self, hash): """ Looks up a token by hash Args: hash (UInt160): The token to look up Returns: SmartContractEvent: A smart contract event with a contract that is an NEP5 Token """ tokens_snapshot = self.db.prefixed_db(NotificationPrefix.PREFIX_TOKEN).snapshot() try: val = tokens_snapshot.get(hash.ToBytes()) if val: event = SmartContractEvent.FromByteArray(val) return event except Exception as e: logger.error("Smart contract event with contract hash %s not found: %s " % (hash.ToString(), e)) return None
def Storage_Put(self, engine: ExecutionEngine): context = None try: context = engine.CurrentContext.EvaluationStack.Pop().GetInterface( neo.SmartContract.StorageContext.StorageContext) except Exception as e: return False if not self.CheckStorageContext(context): return False key = engine.CurrentContext.EvaluationStack.Pop().GetByteArray() if len(key) > 1024: return False value = engine.CurrentContext.EvaluationStack.Pop().GetByteArray() storage_key = StorageKey(script_hash=context.ScriptHash, key=key) item = self.Snapshot.Storages.GetAndChange(storage_key.ToArray(), lambda: StorageItem()) item.Value = value keystr = key valStr = bytearray(item.Value) if type(engine) == ExecutionEngine: test_mode = False else: test_mode = engine.testMode self.events_to_dispatch.append( SmartContractEvent( SmartContractEvent.STORAGE_PUT, ContractParameter(ContractParameterType.String, '%s -> %s' % (keystr, valStr)), context.ScriptHash, GetBlockchain().Height + 1, engine.ScriptContainer.Hash if engine.ScriptContainer else None, test_mode=test_mode)) return True
def Runtime_Log(self, engine: ExecutionEngine): message = engine.CurrentContext.EvaluationStack.Pop().GetString() hash = UInt160(data=engine.CurrentContext.ScriptHash()) tx_hash = None if engine.ScriptContainer: tx_hash = engine.ScriptContainer.Hash engine.write_log(str(message)) # Build and emit smart contract event self.events_to_dispatch.append(SmartContractEvent(SmartContractEvent.RUNTIME_LOG, ContractParameter(ContractParameterType.String, value=message), hash, Blockchain.Default().Height + 1, tx_hash, test_mode=engine.testMode)) return True
def get_by_block(self, block_number): """ Look up notifications for a block Args: block_number (int): height of block to search for notifications Returns: list: a list of notifications """ blocklist_snapshot = self.db.getPrefixedDB( NotificationPrefix.PREFIX_BLOCK).createSnapshot() block_bytes = block_number.to_bytes(4, 'little') results = [] with blocklist_snapshot.db.openIter( DBProperties(prefix=block_bytes, include_key=False)) as it: for val in it: event = SmartContractEvent.FromByteArray(val) results.append(event) return results
def ExecutionCompleted(self, engine, success, error=None): height = Blockchain.Default().Height + 1 tx_hash = None if engine.ScriptContainer: tx_hash = engine.ScriptContainer.Hash if not tx_hash: tx_hash = UInt256(data=bytearray(32)) entry_script = None try: # get the first script that was executed # this is usually the script that sets up the script to be executed entry_script = UInt160(data=engine.ExecutedScriptHashes[0]) # ExecutedScriptHashes[1] will usually be the first contract executed if len(engine.ExecutedScriptHashes) > 1: entry_script = UInt160(data=engine.ExecutedScriptHashes[1]) except Exception as e: logger.error("Could not get entry script: %s " % e) payload = ContractParameter(ContractParameterType.Array, value=[]) for item in engine.ResultStack.Items: payload.Value.append(ContractParameter.ToParameter(item)) if success: # dispatch all notify events, along with the success of the contract execution for notify_event_args in self.notifications: self.events_to_dispatch.append(NotifyEvent(SmartContractEvent.RUNTIME_NOTIFY, notify_event_args.State, notify_event_args.ScriptHash, height, tx_hash, success, engine.testMode)) if engine.Trigger == Application: self.events_to_dispatch.append(SmartContractEvent(SmartContractEvent.EXECUTION_SUCCESS, payload, entry_script, height, tx_hash, success, engine.testMode)) else: self.events_to_dispatch.append(SmartContractEvent(SmartContractEvent.VERIFICATION_SUCCESS, payload, entry_script, height, tx_hash, success, engine.testMode)) else: # when a contract exits in a faulted state # we should display that in the notification if not error: error = 'Execution exited in a faulted state. Any payload besides this message contained in this event is the contents of the EvaluationStack of the current script context.' payload.Value.append(ContractParameter(ContractParameterType.String, error)) # If we do not add the eval stack, then exceptions that are raised in a contract # are not displayed to the event consumer [payload.Value.append(ContractParameter.ToParameter(item)) for item in engine.CurrentContext.EvaluationStack.Items] if engine.Trigger == Application: self.events_to_dispatch.append( SmartContractEvent(SmartContractEvent.EXECUTION_FAIL, payload, entry_script, height, tx_hash, success, engine.testMode)) else: self.events_to_dispatch.append( SmartContractEvent(SmartContractEvent.VERIFICATION_FAIL, payload, entry_script, height, tx_hash, success, engine.testMode)) self.notifications = []
def ExecutionCompleted(self, engine, success, error=None): height = Blockchain.Default().Height + 1 tx_hash = None if engine.ScriptContainer: tx_hash = engine.ScriptContainer.Hash if not tx_hash: tx_hash = UInt256(data=bytearray(32)) entry_script = None try: # get the first script that was executed # this is usually the script that sets up the script to be executed entry_script = UInt160(data=engine.ExecutedScriptHashes[0]) # ExecutedScriptHashes[1] will usually be the first contract executed if len(engine.ExecutedScriptHashes) > 1: entry_script = UInt160(data=engine.ExecutedScriptHashes[1]) except Exception as e: logger.error("Could not get entry script: %s " % e) payload = ContractParameter(ContractParameterType.Array, value=[]) for item in engine.ResultStack.Items: payload.Value.append(ContractParameter.ToParameter(item)) if success: # dispatch all notify events, along with the success of the contract execution for notify_event_args in self.notifications: self.events_to_dispatch.append( NotifyEvent(SmartContractEvent.RUNTIME_NOTIFY, notify_event_args.State, notify_event_args.ScriptHash, height, tx_hash, success, engine.testMode)) if engine.Trigger == Application: self.events_to_dispatch.append( SmartContractEvent(SmartContractEvent.EXECUTION_SUCCESS, payload, entry_script, height, tx_hash, success, engine.testMode)) else: self.events_to_dispatch.append( SmartContractEvent(SmartContractEvent.VERIFICATION_SUCCESS, payload, entry_script, height, tx_hash, success, engine.testMode)) else: payload.Value.append( ContractParameter(ContractParameterType.String, error)) payload.Value.append( ContractParameter(ContractParameterType.String, engine._VMState)) if engine.Trigger == Application: self.events_to_dispatch.append( SmartContractEvent(SmartContractEvent.EXECUTION_FAIL, payload, entry_script, height, tx_hash, success, engine.testMode)) else: self.events_to_dispatch.append( SmartContractEvent(SmartContractEvent.VERIFICATION_FAIL, payload, entry_script, height, tx_hash, success, engine.testMode)) self.notifications = []