def Contract_Create(self, engine):

        script = engine.EvaluationStack.Pop().GetByteArray()

        if len(script) > 1024 * 1024:
            return False

        param_list = engine.EvaluationStack.Pop().GetByteArray()
        if len(param_list) > 252:
            return False

        return_type = int(engine.EvaluationStack.Pop().GetBigInteger())

        contract_properties = int(engine.EvaluationStack.Pop().GetBigInteger())

        if len(engine.EvaluationStack.Peek().GetByteArray()) > 252:
            return False

        name = engine.EvaluationStack.Pop().GetByteArray()

        if len(engine.EvaluationStack.Peek().GetByteArray()) > 252:
            return False
        code_version = engine.EvaluationStack.Pop().GetByteArray()

        if len(engine.EvaluationStack.Peek().GetByteArray()) > 252:
            return False
        author = engine.EvaluationStack.Pop().GetByteArray()

        if len(engine.EvaluationStack.Peek().GetByteArray()) > 252:
            return False
        email = engine.EvaluationStack.Pop().GetByteArray()

        if len(engine.EvaluationStack.Peek().GetByteArray()) > 65536:
            return False

        description = engine.EvaluationStack.Pop().GetByteArray()

        hash = Crypto.ToScriptHash(script, unhex=False)

        contract = self._contracts.TryGet(hash.ToBytes())

        if contract is None:

            code = FunctionCode(script=script,
                                param_list=param_list,
                                return_type=return_type,
                                contract_properties=contract_properties)

            contract = ContractState(code, contract_properties, name,
                                     code_version, author, email, description)

            self._contracts.GetAndChange(code.ScriptHash().ToBytes(), contract)

            self._contracts_created[hash.ToBytes()] = UInt160(
                data=engine.CurrentContext.ScriptHash())

        engine.EvaluationStack.PushT(StackItem.FromInterface(contract))

        self.events_to_dispatch.append(
            SmartContractEvent(SmartContractEvent.CONTRACT_CREATED, [contract],
                               hash,
                               Blockchain.Default().Height,
                               engine.ScriptContainer.Hash
                               if engine.ScriptContainer else None,
                               test_mode=engine.testMode))
        return True
Exemplo n.º 2
0
class SmartContractEvent(SerializableMixin):
    """
    SmartContractEvent is sent as argument to all smart contract event handlers. It
    includes all the information about the current event, such as type, payload,
    contract hash, transaction hash, and block number.

    - event_type (str)
    - contract_hash (UInt160)
    - tx_hash (UInt256)
    - block_number (int)
    - event_payload (object[])
    - execution_success (bool)
    - test_mode (bool)

    `event_payload` is always a list of object, depending on what data types you sent
    in the smart contract.
    """
    RUNTIME_NOTIFY = "SmartContract.Runtime.Notify"  # payload: object[]
    RUNTIME_LOG = "SmartContract.Runtime.Log"  # payload: bytes

    EXECUTION = "SmartContract.Execution.*"
    EXECUTION_INVOKE = "SmartContract.Execution.Invoke"
    EXECUTION_SUCCESS = "SmartContract.Execution.Success"
    EXECUTION_FAIL = "SmartContract.Execution.Fail"

    VERIFICATION = "SmartContract.Verification.*"
    VERIFICATION_SUCCESS = "SmartContract.Verification.Success"
    VERIFICATION_FAIL = "SmartContract.Verification.Fail"

    STORAGE = "SmartContract.Storage.*"
    STORAGE_GET = "SmartContract.Storage.Get"
    STORAGE_PUT = "SmartContract.Storage.Put"
    STORAGE_DELETE = "SmartContract.Storage.Delete"

    CONTRACT = "SmartContract.Contract.*"
    CONTRACT_CREATED = "SmartContract.Contract.Create"
    CONTRACT_MIGRATED = "SmartContract.Contract.Migrate"
    CONTRACT_DESTROY = "SmartContract.Contract.Destroy"

    event_type = None
    event_payload = None
    contract_hash = None
    block_number = None
    tx_hash = None
    execution_success = None
    test_mode = None

    contract = None
    token = None

    def __init__(self,
                 event_type,
                 event_payload,
                 contract_hash,
                 block_number,
                 tx_hash,
                 execution_success=False,
                 test_mode=False):
        self.event_type = event_type
        self.event_payload = event_payload
        self.contract_hash = contract_hash
        self.block_number = block_number
        self.tx_hash = tx_hash
        self.execution_success = execution_success
        self.test_mode = test_mode
        self.token = None

        if not self.event_payload:
            self.event_payload = []

        if self.event_type in [
                SmartContractEvent.CONTRACT_CREATED,
                SmartContractEvent.CONTRACT_MIGRATED
        ]:
            if len(self.event_payload) and isinstance(self.event_payload[0],
                                                      ContractState):
                self.contract = self.event_payload[0]

    def Serialize(self, writer):
        writer.WriteVarString(self.event_type.encode('utf-8'))
        writer.WriteUInt160(self.contract_hash)
        writer.WriteUInt32(self.block_number)
        writer.WriteUInt256(self.tx_hash)
        self.SerializePayload(writer)

    def SerializePayload(self, writer):

        if self.event_type in [
                SmartContractEvent.CONTRACT_CREATED,
                SmartContractEvent.CONTRACT_MIGRATED
        ] and self.contract:
            self.contract.Serialize(writer)
            if self.token:
                self.token.Serialize(writer)

    def Deserialize(self, reader):
        self.event_type = reader.ReadVarString().decode('utf-8')
        self.contract_hash = reader.ReadUInt160()
        self.block_number = reader.ReadUInt32()
        self.tx_hash = reader.ReadUInt256()
        self.DeserializePayload(reader)

    def DeserializePayload(self, reader):
        if self.event_type in [
                SmartContractEvent.CONTRACT_CREATED,
                SmartContractEvent.CONTRACT_MIGRATED
        ]:
            self.contract = ContractState()
            self.contract.Deserialize(reader)
            try:
                from neo.Wallets.NEP5Token import NEP5Token
                token = NEP5Token(binascii.hexlify(self.contract.Code.Script))
                token.Deserialize(reader)
                self.token = token
            except Exception as e:
                logger.error("Couldnt deserialize token %s " % e)

    def __str__(self):
        return "SmartContractEvent(event_type=%s, event_payload=%s, contract_hash=%s, block_number=%s, tx_hash=%s, execution_success=%s, test_mode=%s)" \
               % (self.event_type, self.event_payload, self.contract_hash, self.block_number, self.tx_hash, self.execution_success, self.test_mode)

    def ToByteArray(self):
        stream = StreamManager.GetStream()
        writer = BinaryWriter(stream)
        self.Serialize(writer)
        out = stream.getvalue()
        StreamManager.ReleaseStream(stream)
        return out

    @staticmethod
    def FromByteArray(data):
        stream = StreamManager.GetStream(data=data)
        reader = BinaryReader(stream)

        etype = reader.ReadVarString().decode('utf-8')
        reader.stream.seek(0)

        if etype == SmartContractEvent.RUNTIME_NOTIFY:
            event = NotifyEvent(None, None, None, None, None)
        else:
            event = SmartContractEvent(None, None, None, None, None)

        event.Deserialize(reader)
        StreamManager.ReleaseStream(stream)
        return event

    def CheckIsNEP5(self):
        if self.contract and self.contract.IsNEP5Contract:
            self.token = self.contract._nep_token

    def ToJson(self):

        jsn = {
            'type': self.event_type,
            'contract': self.contract_hash.To0xString(),
            'block': self.block_number,
            'tx': self.tx_hash.To0xString()
        }

        if self.event_type in [
                SmartContractEvent.CONTRACT_CREATED,
                SmartContractEvent.CONTRACT_MIGRATED
        ]:
            jsn['contract'] = self.contract.ToJson()

        if self.token:
            jsn['token'] = self.token.ToJson()

        return jsn
Exemplo n.º 3
0
    def Persist(self, block):

        sn = self._db.snapshot()

        accounts = DBCollection(self._db, sn, DBPrefix.ST_Account,
                                AccountState)
        unspentcoins = DBCollection(self._db, sn, DBPrefix.ST_Coin,
                                    UnspentCoinState)
        spentcoins = DBCollection(self._db, sn, DBPrefix.ST_SpentCoin,
                                  SpentCoinState)
        assets = DBCollection(self._db, sn, DBPrefix.ST_Asset, AssetState)
        validators = DBCollection(self._db, sn, DBPrefix.ST_Validator,
                                  ValidatorState)
        contracts = DBCollection(self._db, sn, DBPrefix.ST_Contract,
                                 ContractState)
        storages = DBCollection(self._db, sn, DBPrefix.ST_Storage, StorageItem)

        amount_sysfee = self.GetSysFeeAmount(
            block.PrevHash) + block.TotalFees().value
        amount_sysfee_bytes = amount_sysfee.to_bytes(8, 'little')

        with self._db.write_batch() as wb:
            for tx in block.Transactions:

                unspentcoinstate = UnspentCoinState.FromTXOutputsConfirmed(
                    tx.outputs)
                unspentcoins.Add(tx.Hash.ToBytes(), unspentcoinstate)

                # go through all the accounts in the tx outputs
                for output in tx.outputs:
                    account = accounts.GetAndChange(
                        output.AddressBytes, AccountState(output.ScriptHash))

                    if account.HasBalance(output.AssetId):
                        account.AddToBalance(output.AssetId, output.Value)
                    else:
                        account.SetBalanceFor(output.AssetId, output.Value)

                # go through all tx inputs
                unique_tx_input_hashes = []
                for input in tx.inputs:
                    if input.PrevHash not in unique_tx_input_hashes:
                        unique_tx_input_hashes.append(input.PrevHash)

                for txhash in unique_tx_input_hashes:
                    prevTx, height = self.GetTransaction(txhash.ToBytes())
                    coin_refs_by_hash = [
                        coinref for coinref in tx.inputs
                        if coinref.PrevHash.ToBytes() == txhash.ToBytes()
                    ]
                    for input in coin_refs_by_hash:

                        uns = unspentcoins.GetAndChange(
                            input.PrevHash.ToBytes())
                        uns.OrEqValueForItemAt(input.PrevIndex,
                                               CoinState.Spent)

                        if prevTx.outputs[input.PrevIndex].AssetId.ToBytes(
                        ) == Blockchain.SystemShare().Hash.ToBytes():
                            sc = spentcoins.GetAndChange(
                                input.PrevHash.ToBytes(),
                                SpentCoinState(input.PrevHash, height, []))
                            sc.Items.append(
                                SpentCoinItem(input.PrevIndex, block.Index))

                        output = prevTx.outputs[input.PrevIndex]
                        acct = accounts.GetAndChange(
                            prevTx.outputs[input.PrevIndex].AddressBytes,
                            AccountState(output.ScriptHash))
                        assetid = prevTx.outputs[input.PrevIndex].AssetId
                        acct.SubtractFromBalance(
                            assetid, prevTx.outputs[input.PrevIndex].Value)

                # do a whole lotta stuff with tx here...
                if tx.Type == TransactionType.RegisterTransaction:

                    asset = AssetState(tx.Hash, tx.AssetType, tx.Name,
                                       tx.Amount, Fixed8(0), tx.Precision,
                                       Fixed8(0), Fixed8(0),
                                       UInt160(data=bytearray(20)), tx.Owner,
                                       tx.Admin, tx.Admin,
                                       block.Index + 2 * 2000000, False)

                    assets.Add(tx.Hash.ToBytes(), asset)

                elif tx.Type == TransactionType.IssueTransaction:

                    txresults = [
                        result for result in tx.GetTransactionResults()
                        if result.Amount.value < 0
                    ]
                    for result in txresults:
                        asset = assets.GetAndChange(result.AssetId.ToBytes())
                        asset.Available = asset.Available - result.Amount

                elif tx.Type == TransactionType.ClaimTransaction:

                    for input in tx.Claims:

                        sc = spentcoins.TryGet(input.PrevHash.ToBytes())
                        if sc and sc.HasIndex(input.PrevIndex):
                            sc.DeleteIndex(input.PrevIndex)
                            spentcoins.GetAndChange(input.PrevHash.ToBytes())

                elif tx.Type == TransactionType.EnrollmentTransaction:

                    validator = validators.GetAndChange(
                        tx.PublicKey, ValidatorState(pub_key=tx.PublicKey))
                    #                        logger.info("VALIDATOR %s " % validator.ToJson())

                elif tx.Type == TransactionType.StateTransaction:
                    # @TODO Implement persistence for State Descriptors
                    pass

                elif tx.Type == TransactionType.PublishTransaction:

                    contract = ContractState(tx.Code, tx.NeedStorage, tx.Name,
                                             tx.CodeVersion, tx.Author,
                                             tx.Email, tx.Description)

                    contracts.GetAndChange(tx.Code.ScriptHash().ToBytes(),
                                           contract)

                elif tx.Type == TransactionType.InvocationTransaction:

                    script_table = CachedScriptTable(contracts)
                    service = StateMachine(accounts,
                                           validators,
                                           assets,
                                           contracts,
                                           storages,
                                           wb=wb)

                    engine = ApplicationEngine(
                        trigger_type=TriggerType.Application,
                        container=tx,
                        table=script_table,
                        service=service,
                        gas=tx.Gas,
                        testMode=True)

                    engine.LoadScript(tx.Script, False)

                    # normally, this function does not return true/false
                    # for testing purposes, we try to execute and if an exception is raised
                    # we will return false, otherwise if success return true

                    # this is different than the 'success' bool returned by engine.Execute()
                    # the 'success' bool returned by engine.Execute() is a value indicating
                    # wether or not the invocation was successful, and if so, we then commit
                    # the changes made by the contract to the database
                    try:
                        success = engine.Execute()
                        # service.ExecutionCompleted(engine, success)
                        return True
                    except Exception as e:
                        # service.ExecutionCompleted(self, False, e)
                        return False
Exemplo n.º 4
0
    def Contract_Migrate(self, engine):

        script = engine.EvaluationStack.Pop().GetByteArray()

        if len(script) > 1024 * 1024:
            return False

        param_list = engine.EvaluationStack.Pop().GetByteArray()

        if len(param_list) > 252:
            return False

        return_type = int(engine.EvaluationStack.Pop().GetBigInteger())

        contract_properties = engine.EvaluationStack.Pop().GetBigInteger()

        if len(engine.EvaluationStack.Peek().GetByteArray()) > 252:
            return False

        name = engine.EvaluationStack.Pop().GetByteArray().decode('utf-8')

        if len(engine.EvaluationStack.Peek().GetByteArray()) > 252:
            return False
        version = engine.EvaluationStack.Pop().GetByteArray().decode('utf-8')

        if len(engine.EvaluationStack.Peek().GetByteArray()) > 252:
            return False
        author = engine.EvaluationStack.Pop().GetByteArray().decode('utf-8')

        if len(engine.EvaluationStack.Peek().GetByteArray()) > 252:
            return False
        email = engine.EvaluationStack.Pop().GetByteArray().decode('utf-8')

        if len(engine.EvaluationStack.Peek().GetByteArray()) > 65536:
            return False
        description = engine.EvaluationStack.Pop().GetByteArray().decode(
            'utf-8')

        hash = Crypto.ToScriptHash(script, unhex=False)

        contract = self._contracts.TryGet(hash.ToBytes())

        if contract is None:

            code = FunctionCode(script=script,
                                param_list=param_list,
                                return_type=return_type)

            contract = ContractState(code=code,
                                     contract_properties=contract_properties,
                                     name=name,
                                     version=version,
                                     author=author,
                                     email=email,
                                     description=description)

            self._contracts.Add(hash.ToBytes(), contract)

            self._contracts_created[hash.ToBytes()] = UInt160(
                data=engine.CurrentContext.ScriptHash())

            if contract.HasStorage:
                for key, val in self._storages.Find(
                        engine.CurrentContext.ScriptHash()).items():
                    key = StorageKey(script_hash=hash, key=key)
                    item = StorageItem(val)
                    self._storages.Add(key.ToArray(), item)

        engine.EvaluationStack.PushT(StackItem.FromInterface(contract))

        self.events_to_dispatch.append(
            SmartContractEvent(SmartContractEvent.CONTRACT_MIGRATED,
                               ContractParameter(
                                   ContractParameterType.InteropInterface,
                                   contract),
                               hash,
                               Blockchain.Default().Height + 1,
                               engine.ScriptContainer.Hash
                               if engine.ScriptContainer else None,
                               test_mode=engine.testMode))

        return self.Contract_Destroy(engine)
Exemplo n.º 5
0
    def Persist(self, block):

        self._persisting_block = block

        accounts = DBCollection(self._db, DBPrefix.ST_Account, AccountState)
        unspentcoins = DBCollection(self._db, DBPrefix.ST_Coin,
                                    UnspentCoinState)
        spentcoins = DBCollection(self._db, DBPrefix.ST_SpentCoin,
                                  SpentCoinState)
        assets = DBCollection(self._db, DBPrefix.ST_Asset, AssetState)
        validators = DBCollection(self._db, DBPrefix.ST_Validator,
                                  ValidatorState)
        contracts = DBCollection(self._db, DBPrefix.ST_Contract, ContractState)
        storages = DBCollection(self._db, DBPrefix.ST_Storage, StorageItem)

        amount_sysfee = self.GetSysFeeAmount(
            block.PrevHash) + block.TotalFees().value
        amount_sysfee_bytes = amount_sysfee.to_bytes(8, 'little')

        to_dispatch = []

        with self._db.write_batch() as wb:

            wb.put(DBPrefix.DATA_Block + block.Hash.ToBytes(),
                   amount_sysfee_bytes + block.Trim())

            for tx in block.Transactions:

                wb.put(DBPrefix.DATA_Transaction + tx.Hash.ToBytes(),
                       block.IndexBytes() + tx.ToArray())

                # go through all outputs and add unspent coins to them

                unspentcoinstate = UnspentCoinState.FromTXOutputsConfirmed(
                    tx.outputs)
                unspentcoins.Add(tx.Hash.ToBytes(), unspentcoinstate)

                # go through all the accounts in the tx outputs
                for output in tx.outputs:
                    account = accounts.GetAndChange(
                        output.AddressBytes, AccountState(output.ScriptHash))

                    if account.HasBalance(output.AssetId):
                        account.AddToBalance(output.AssetId, output.Value)
                    else:
                        account.SetBalanceFor(output.AssetId, output.Value)

                # go through all tx inputs
                unique_tx_input_hashes = []
                for input in tx.inputs:
                    if input.PrevHash not in unique_tx_input_hashes:
                        unique_tx_input_hashes.append(input.PrevHash)

                for txhash in unique_tx_input_hashes:
                    prevTx, height = self.GetTransaction(txhash.ToBytes())
                    coin_refs_by_hash = [
                        coinref for coinref in tx.inputs
                        if coinref.PrevHash.ToBytes() == txhash.ToBytes()
                    ]
                    for input in coin_refs_by_hash:

                        uns = unspentcoins.GetAndChange(
                            input.PrevHash.ToBytes())
                        uns.OrEqValueForItemAt(input.PrevIndex,
                                               CoinState.Spent)

                        if prevTx.outputs[input.PrevIndex].AssetId.ToBytes(
                        ) == Blockchain.SystemShare().Hash.ToBytes():
                            sc = spentcoins.GetAndChange(
                                input.PrevHash.ToBytes(),
                                SpentCoinState(input.PrevHash, height, []))
                            sc.Items.append(
                                SpentCoinItem(input.PrevIndex, block.Index))

                        output = prevTx.outputs[input.PrevIndex]
                        acct = accounts.GetAndChange(
                            prevTx.outputs[input.PrevIndex].AddressBytes,
                            AccountState(output.ScriptHash))
                        assetid = prevTx.outputs[input.PrevIndex].AssetId
                        acct.SubtractFromBalance(
                            assetid, prevTx.outputs[input.PrevIndex].Value)

                # do a whole lotta stuff with tx here...
                if tx.Type == TransactionType.RegisterTransaction:
                    asset = AssetState(tx.Hash, tx.AssetType, tx.Name,
                                       tx.Amount, Fixed8(0), tx.Precision,
                                       Fixed8(0), Fixed8(0),
                                       UInt160(data=bytearray(20)), tx.Owner,
                                       tx.Admin, tx.Admin,
                                       block.Index + 2 * 2000000, False)

                    assets.Add(tx.Hash.ToBytes(), asset)

                elif tx.Type == TransactionType.IssueTransaction:

                    txresults = [
                        result for result in tx.GetTransactionResults()
                        if result.Amount.value < 0
                    ]
                    for result in txresults:
                        asset = assets.GetAndChange(result.AssetId.ToBytes())
                        asset.Available = asset.Available - result.Amount

                elif tx.Type == TransactionType.ClaimTransaction:
                    for input in tx.Claims:

                        sc = spentcoins.TryGet(input.PrevHash.ToBytes())
                        if sc and sc.HasIndex(input.PrevIndex):
                            sc.DeleteIndex(input.PrevIndex)
                            spentcoins.GetAndChange(input.PrevHash.ToBytes())

                elif tx.Type == TransactionType.EnrollmentTransaction:
                    newvalidator = ValidatorState(pub_key=tx.PublicKey)
                    validators.GetAndChange(tx.PublicKey.ToBytes(),
                                            newvalidator)
                elif tx.Type == TransactionType.StateTransaction:
                    # @TODO Implement persistence for State Descriptors
                    pass

                elif tx.Type == TransactionType.PublishTransaction:
                    contract = ContractState(tx.Code, tx.NeedStorage, tx.Name,
                                             tx.CodeVersion, tx.Author,
                                             tx.Email, tx.Description)

                    contracts.GetAndChange(tx.Code.ScriptHash().ToBytes(),
                                           contract)
                elif tx.Type == TransactionType.InvocationTransaction:

                    script_table = CachedScriptTable(contracts)
                    service = StateMachine(accounts, validators, assets,
                                           contracts, storages, wb)

                    engine = ApplicationEngine(
                        trigger_type=TriggerType.Application,
                        container=tx,
                        table=script_table,
                        service=service,
                        gas=tx.Gas,
                        testMode=False)

                    engine.LoadScript(tx.Script)

                    try:
                        success = engine.Execute()
                        service.ExecutionCompleted(engine, success)

                    except Exception as e:
                        service.ExecutionCompleted(engine, False, e)

                    to_dispatch = to_dispatch + service.events_to_dispatch
                else:

                    if tx.Type != b'\x00' and tx.Type != 128:
                        logger.info("TX Not Found %s " % tx.Type)

            # do save all the accounts, unspent, coins, validators, assets, etc
            # now sawe the current sys block

            # filter out accounts to delete then commit
            for key, account in accounts.Current.items():
                if not account.IsFrozen and len(
                        account.Votes) == 0 and account.AllBalancesZeroOrLess(
                        ):
                    accounts.Remove(key)

            accounts.Commit(wb)

            # filte out unspent coins to delete then commit
            for key, unspent in unspentcoins.Current.items():
                if unspent.IsAllSpent:
                    unspentcoins.Remove(key)
            unspentcoins.Commit(wb)

            # filter out spent coins to delete then commit to db
            for key, spent in spentcoins.Current.items():
                if len(spent.Items) == 0:
                    spentcoins.Remove(key)
            spentcoins.Commit(wb)

            # commit validators
            validators.Commit(wb)

            # commit assets
            assets.Commit(wb)

            # commit contracts
            contracts.Commit(wb)

            wb.put(DBPrefix.SYS_CurrentBlock,
                   block.Hash.ToBytes() + block.IndexBytes())
            self._current_block_height = block.Index
            self._persisting_block = None

            self.TXProcessed += len(block.Transactions)

        for event in to_dispatch:
            events.emit(event.event_type, event)