Example #1
0
    def CalcChange(self, change_addr=None):
        """
        Calculates the change output(s). NOTE: Assumes all other outputs have been added.

        Args:
            change_addr: (str, optional) specify a change address. NOTE: Defaults to the sourceAddress.
        """
        if not change_addr:
            change_addr = self.SOURCE_SCRIPTHASH
        if change_addr != self.SOURCE_SCRIPTHASH:
            change_hash = Helper.AddrStrToScriptHash(
                change_addr)  # also verifies if the address is valid
        else:
            change_hash = change_addr

        if not self.outputs:
            raise RawTXError(
                "Please specify outputs prior to creating change output(s).")

        neo = []
        gas = []
        for output in self.outputs:
            if output.AssetId == UInt256.ParseString(self.neo_asset_id):
                neo.append(output.Value.value)
            elif output.AssetId == UInt256.ParseString(self.gas_asset_id):
                gas.append(output.Value.value)
        if self.SystemFee() > Fixed8.Zero():
            gas.append(self.SystemFee().value)
        if self._network_fee:
            if self._network_fee > Fixed8.Zero():
                gas.append(self._network_fee.value)
        neo_total = 0
        gas_total = 0
        for asset in self.BALANCE:
            if asset['asset_hash'] == self.neo_asset_id:
                neo_total = asset['amount']
            elif asset['asset_hash'] == self.gas_asset_id:
                gas_total = asset['amount']
        neo_diff = Fixed8.FromDecimal(neo_total) - Fixed8(sum(neo))
        gas_diff = Fixed8.FromDecimal(gas_total) - Fixed8(sum(gas))

        if neo_diff < Fixed8.Zero() or gas_diff < Fixed8.Zero():
            raise AssetError('Total outputs exceed the available unspents.')

        if neo_diff > Fixed8.Zero():
            self.outputs.append(
                TransactionOutput(AssetId=UInt256.ParseString(
                    self.neo_asset_id),
                                  Value=neo_diff,
                                  script_hash=change_hash))
        if gas_diff > Fixed8.Zero() and Fixed8(sum(gas)) > Fixed8.Zero():
            self.outputs.append(
                TransactionOutput(AssetId=UInt256.ParseString(
                    self.gas_asset_id),
                                  Value=gas_diff,
                                  script_hash=change_hash))
Example #2
0
    def test_parse(self):
        string = '0xcedb5c4e24b1f6fc5b239f2d1049c3229ad5ed05293c696b3740dc236c3f41b4'
        uint256 = UInt256.ParseString(string)
        self.assertIsInstance(uint256, UInt256)
        self.assertEqual(uint256.To0xString(), string)

        string = '9410bd44beb7d6febc9278b028158af2781fcfb40cf2c6067b3525d24eff19f6'
        uint256 = UInt256.ParseString(string)
        self.assertIsInstance(uint256, UInt256)
        self.assertEqual(uint256.ToString(), string)

        string = '9410bd44beb7d6febc9278b028158af2781fcfb40cf2c6067b3525d24eff19f'
        with self.assertRaises(ValueError) as context:
            uint256 = UInt256.ParseString(string)
            self.assertIn(f"Invalid UInt256 input: {len(string)} chars != 64 chars", context)
Example #3
0
    def AddOutput(self, asset, to_addr, amount):
        """
        Specify an output for the transaction.
        NOTE: Can be used multiple times to create multiple outputs.

        Args:
            asset: (str) the asset name or asset hash
            to_addr: (str) the destination NEO address (e.g. 'AJQ6FoaSXDFzA6wLnyZ1nFN7SGSN2oNTc3')
            amount: (int/decimal) the amount of the asset to send
        """
        if asset[0:1] == "0x":
            asset == asset[2:]
        if asset.lower() == "neo":
            assetId = self.neo_asset_id
        elif asset == self.neo_asset_id:
            assetId = self.neo_asset_id
        elif asset.lower() == "gas":
            assetId = self.gas_asset_id
        elif asset == self.gas_asset_id:
            assetId = self.gas_asset_id
        else:
            raise AssetError(
                f'Asset {asset} not found. If trying to send tokens use the `buildTokenTransfer` function.'
            )

        dest_scripthash = Helper.AddrStrToScriptHash(
            to_addr)  # also verifies if the address is valid

        if float(amount) == 0:
            raise ValueError('Amount cannot be 0.')
        f8amount = Fixed8.TryParse(amount, require_positive=True)
        if f8amount is None:
            raise ValueError('Invalid amount format.')
        elif assetId == self.neo_asset_id and (f8amount.value /
                                               Fixed8.D) != f8amount.ToInt():
            raise ValueError('Incorrect amount precision.')

        # check if the outputs exceed the available unspents
        subtotal = []
        if self.outputs:
            for output in self.outputs:
                if output.AssetId == assetId:
                    subtotal.append(output.Value.value)
        total = f8amount.value + sum(subtotal)
        total = float(Fixed8(total).ToString())
        for asset in self.BALANCE:
            if assetId == asset['asset_hash']:
                if total > asset['amount']:
                    raise AssetError(
                        'Total outputs exceed the available unspents.')

        self.outputs.append(
            TransactionOutput(AssetId=UInt256.ParseString(assetId),
                              Value=f8amount,
                              script_hash=dest_scripthash))
Example #4
0
    def StaticAssetState(assetId):
        neo = AssetState()
        neo.AssetId = UInt256.ParseString(
            "0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b"
        )
        neo.AssetType = 0x00

        gas = AssetState()
        gas.AssetId = UInt256.ParseString(
            "0x602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"
        )
        gas.AssetType = 0x01

        if assetId == neo.AssetId:
            return neo

        elif assetId == gas.AssetId:
            return gas

        else:
            return None
    def test_interop_get_bad_transaction_height(self):
        u256 = UInt256.ParseString('8be9660512991d36e016b8ced6fda5d611d26a0f6e2faaaf1f379496edb33956')

        hash = StackItem.New(u256.Data)

        self.econtext.EvaluationStack.PushT(hash)
        self.engine.InvocationStack.PushT(self.econtext)
        self.state_reader.Blockchain_GetTransactionHeight(self.engine)

        height = self.econtext.EvaluationStack.Pop().GetBigInteger()

        self.assertEqual(height, -1)
    def test_interop_get_transaction(self):
        u256 = UInt256.ParseString('8be9660512991d36e016b8ced6fda5d611d26a0f6e2faaaf1f379496edb3395f')

        hash = StackItem.New(u256.Data)

        self.econtext.EvaluationStack.PushT(hash)
        self.engine.InvocationStack.PushT(self.econtext)
        self.state_reader.Blockchain_GetTransaction(self.engine)

        tx = self.econtext.EvaluationStack.Pop().GetInterface()

        self.assertIsInstance(tx, Transaction)
Example #7
0
    def AddClaim(self, claim_addr, to_addr=None):
        """
        Builds a claim transaction for the specified address.

        Args:
            claim_addr: (str) the address from which the claim is being constructed (e.g. 'AJQ6FoaSXDFzA6wLnyZ1nFN7SGSN2oNTc3'). NOTE: Claimed GAS is sent to the claim_addr by default
            to_addr: (str, optional) specify a different destination NEO address (e.g. 'AJQ6FoaSXDFzA6wLnyZ1nFN7SGSN2oNTc3')
        """
        dest_scripthash = Helper.AddrStrToScriptHash(
            claim_addr)  # also verifies if the address is valid
        self.SOURCE_SCRIPTHASH = dest_scripthash

        url = self._network + self._get_claimable + claim_addr
        res = requests.get(url=url)

        if not res.status_code == 200:
            raise NetworkError(
                'Neoscan request failed. Please check your internet connection.'
            )

        res = res.json()
        available = res["unclaimed"]
        if available == 0:
            raise AssetError(
                f"Address {claim_addr} has 0 unclaimed GAS. Please ensure the correct network is selected or specify a difference source address."
            )

        for ref in res['claimable']:
            self.Claims.append(
                CoinReference(prev_hash=UInt256.ParseString(ref['txid']),
                              prev_index=ref['n']))

        if to_addr:
            dest_scripthash = Helper.AddrStrToScriptHash(
                to_addr)  # also verifies if the address is valid

        self.outputs.append(
            TransactionOutput(AssetId=UInt256.ParseString(self.gas_asset_id),
                              Value=Fixed8.FromDecimal(available),
                              script_hash=dest_scripthash))
Example #8
0
    def References(self):
        """
        Get all references.

        Returns:
            dict:
                Key (UInt256): input PrevHash
                Value (TransactionOutput): object.
        """
        if self.__references is None:
            refs = {}
            # group by the input prevhash
            for hash, group in groupby(self.inputs, lambda x: x.PrevHash):
                url = self._network + self._get_transaction + hash.ToString()
                tx = requests.get(url=url)

                if not tx.status_code == 200:
                    raise NetworkError(
                        'Neoscan request failed. Please check your internet connection.'
                    )
                tx = tx.json()

                if tx is not None:
                    for input in group:
                        t = tx['vouts'][input.PrevIndex]
                        if t['asset'].lower() == 'neo':
                            asset = UInt256.ParseString(self.neo_asset_id)
                        elif t['asset'].lower() == 'gas':
                            asset = UInt256.ParseString(self.gas_asset_id)
                        refs[input] = TransactionOutput(
                            AssetId=asset,
                            Value=Fixed8.FromDecimal(t['value']),
                            script_hash=Helper.AddrStrToScriptHash(
                                t['address_hash']))

            self.__references = refs

        return self.__references
Example #9
0
    def get_by_tx(self, request, tx_hash):
        request.setHeader('Content-Type', 'application/json')

        bc = Blockchain.Default()  # type: Blockchain
        notifications = []
        try:
            hash = UInt256.ParseString(tx_hash)
            tx, height = bc.GetTransaction(hash)
            if not tx:
                return self.format_message("Could not find transaction for hash %s" % (tx_hash))
            block_notifications = self.notif.get_by_block(height)
            for n in block_notifications:
                if n.tx_hash == tx.Hash:
                    notifications.append(n)
        except Exception as e:
            logger.info("Could not get tx with hash %s because %s " % (tx_hash, e))
            return self.format_message("Could not get tx with hash %s because %s " % (tx_hash, e))

        return self.format_notifications(request, notifications)
Example #10
0
    def FromJson(json):
        """
        Convert a json object to a ContractParameter object

        Args:
            item (dict): The item to convert to a ContractParameter object

        Returns:
            ContractParameter

        """
        type = ContractParameterType.FromString(json['type'])

        value = json['value']
        param = ContractParameter(type=type, value=None)

        if type == ContractParameterType.Signature or type == ContractParameterType.ByteArray:
            param.Value = bytearray.fromhex(value)

        elif type == ContractParameterType.Boolean:
            param.Value = bool(value)

        elif type == ContractParameterType.Integer:
            param.Value = int(value)

        elif type == ContractParameterType.Hash160:
            param.Value = UInt160.ParseString(value)

        elif type == ContractParameterType.Hash256:
            param.Value = UInt256.ParseString(value)

        # @TODO Not sure if this is working...
        elif type == ContractParameterType.PublicKey:
            param.Value = ECDSA.decode_secp256r1(value).G

        elif type == ContractParameterType.String:
            param.Value = str(value)

        elif type == ContractParameterType.Array:
            val = [ContractParameter.FromJson(item) for item in value]
            param.Value = val

        return param
Example #11
0
    def AddInputs(self, asset):
        """
        Specify inputs for the transaction based on the asset to be sent.
        NOTE: Can be used multiple times if sending multiple assets (i.e. NEO and GAS).

        Args:
            asset: (str) the asset name or asset hash
        """
        if not isinstance(asset, str):
            raise TypeError('Please enter the asset as a string.')

        if not self.BALANCE:
            raise RawTXError(
                'Please specify a source address before adding inputs.')

        if asset[0:1] == "0x":
            asset == asset[2:]
        if asset.lower() == "neo":
            assetId = self.neo_asset_id
        elif asset == self.neo_asset_id:
            assetId = self.neo_asset_id
        elif asset.lower() == "gas":
            assetId = self.gas_asset_id
        elif asset == self.gas_asset_id:
            assetId = self.gas_asset_id
        else:
            raise AssetError(
                f'Asset {asset} not found. If trying to send tokens use the `buildTokenTransfer` function.'
            )

        for asset in self.BALANCE:
            if assetId == asset['asset_hash']:
                if not asset['unspent']:
                    raise AssetError('No unspent assets found.')
                for unspent in asset['unspent']:
                    self.inputs.append(
                        CoinReference(prev_hash=UInt256.ParseString(
                            unspent['txid']),
                                      prev_index=unspent['n']))
        if not self.inputs:
            raise AssetError(
                'No matching assets found at the specified source address.')
Example #12
0
 def execute(self, arguments):
     if len(arguments):
         try:
             txid = UInt256.ParseString(get_arg(arguments))
             tx, height = Blockchain.Default().GetTransaction(txid)
             if height > -1:
                 jsn = tx.ToJson()
                 jsn['height'] = height
                 jsn['unspents'] = [
                     uns.ToJson(tx.outputs.index(uns))
                     for uns in Blockchain.Default().GetAllUnspent(txid)
                 ]
                 print(json.dumps(jsn, indent=4))
                 return jsn
             else:
                 print(f"Could not find transaction for hash {txid}")
                 return
         except Exception:
             print("Could not find transaction from args: %s" % arguments)
             return
     else:
         print("Please specify the required parameter")
         return
Example #13
0
    def execute(self, arguments):
        item = get_arg(arguments)
        if item is not None:
            if item.lower() == "all":
                assets = Blockchain.Default().ShowAllAssets()
                assetlist = []
                for asset in assets:
                    state = Blockchain.Default().GetAssetState(
                        asset.decode('utf-8')).ToJson()
                    asset_dict = {state['name']: state['assetId']}
                    assetlist.append(asset_dict)
                print(json.dumps(assetlist, indent=4))
                return assetlist

            if item.lower() == 'neo':
                assetId = Blockchain.Default().SystemShare().Hash
            elif item.lower() == 'gas':
                assetId = Blockchain.Default().SystemCoin().Hash
            else:
                try:
                    assetId = UInt256.ParseString(item)
                except Exception:
                    print("Could not find asset from args: %s" % arguments)
                    return

            asset = Blockchain.Default().GetAssetState(assetId.ToBytes())

            if asset is not None:
                print(json.dumps(asset.ToJson(), indent=4))
                return asset.ToJson()
            else:
                print("Asset %s not found" % item)
                return
        else:
            print('Please specify the required parameter')
            return
Example #14
0
    def json_rpc_method_handler(self, method, params):

        if method == "getaccountstate":
            acct = Blockchain.Default().GetAccountState(params[0])
            if acct is None:
                try:
                    acct = AccountState(script_hash=Helper.AddrStrToScriptHash(params[0]))
                except Exception as e:
                    raise JsonRpcError(-2146233033, "One of the identified items was in an invalid format.")

            return acct.ToJson()

        elif method == "getassetstate":
            asset_str = params[0]
            if asset_str.lower() == 'neo':
                assetId = Blockchain.Default().SystemShare().Hash
            elif asset_str.lower() == 'gas':
                assetId = Blockchain.Default().SystemCoin().Hash
            else:
                assetId = UInt256.ParseString(params[0])
            asset = Blockchain.Default().GetAssetState(assetId.ToBytes())
            if asset:
                return asset.ToJson()
            raise JsonRpcError(-100, "Unknown asset")

        elif method == "getbestblockhash":
            return '0x%s' % Blockchain.Default().CurrentHeaderHash.decode('utf-8')

        elif method == "getblock":
            # this should work for either str or int
            block = Blockchain.Default().GetBlock(params[0])
            if not block:
                raise JsonRpcError(-100, "Unknown block")
            return self.get_block_output(block, params)

        elif method == "getblockcount":
            return Blockchain.Default().Height + 1

        elif method == "getblockhash":
            height = params[0]
            if height >= 0 and height <= Blockchain.Default().Height:
                return '0x%s' % Blockchain.Default().GetBlockHash(height).decode('utf-8')
            else:
                raise JsonRpcError(-100, "Invalid Height")

        elif method == "getblocksysfee":
            height = params[0]
            if height >= 0 and height <= Blockchain.Default().Height:
                return Blockchain.Default().GetSysFeeAmountByHeight(height)
            else:
                raise JsonRpcError(-100, "Invalid Height")

        elif method == "getconnectioncount":
            return len(NodeLeader.Instance().Peers)

        elif method == "getcontractstate":
            script_hash = UInt160.ParseString(params[0])
            contract = Blockchain.Default().GetContract(script_hash.ToBytes())
            if contract is None:
                raise JsonRpcError(-100, "Unknown contract")
            return contract.ToJson()

        elif method == "getrawmempool":
            return list(map(lambda hash: "0x%s" % hash.decode('utf-8'), NodeLeader.Instance().MemPool.keys()))

        elif method == "getversion":
            return {
                "port": self.port,
                "nonce": NodeLeader.Instance().NodeId,
                "useragent": settings.VERSION_NAME
            }

        elif method == "getrawtransaction":
            tx_id = UInt256.ParseString(params[0])
            tx, height = Blockchain.Default().GetTransaction(tx_id)
            if not tx:
                raise JsonRpcError(-100, "Unknown Transaction")
            return self.get_tx_output(tx, height, params)

        elif method == "getstorage":
            script_hash = UInt160.ParseString(params[0])
            key = binascii.unhexlify(params[1].encode('utf-8'))
            storage_key = StorageKey(script_hash=script_hash, key=key)
            storage_item = Blockchain.Default().GetStorageItem(storage_key)
            if storage_item:
                return storage_item.Value.hex()
            return None

        elif method == "gettransactionheight":
            try:
                hash = UInt256.ParseString(params[0])
            except Exception:
                # throws exception, not anything more specific
                raise JsonRpcError(-100, "Unknown transaction")

            tx, height = Blockchain.Default().GetTransaction(hash)
            if tx:
                return height
            else:
                raise JsonRpcError(-100, "Unknown transaction")

        elif method == "gettxout":
            hash = params[0].encode('utf-8')
            index = params[1]
            utxo = Blockchain.Default().GetUnspent(hash, index)
            if utxo:
                return utxo.ToJson(index)
            else:
                return None

        elif method == "invoke":
            shash = UInt160.ParseString(params[0])
            contract_parameters = [ContractParameter.FromJson(p) for p in params[1]]
            sb = ScriptBuilder()
            sb.EmitAppCallWithJsonArgs(shash, contract_parameters)
            return self.get_invoke_result(sb.ToArray())

        elif method == "invokefunction":
            contract_parameters = []
            if len(params) > 2:
                contract_parameters = [ContractParameter.FromJson(p).ToVM() for p in params[2]]
            sb = ScriptBuilder()
            sb.EmitAppCallWithOperationAndArgs(UInt160.ParseString(params[0]), params[1], contract_parameters)
            return self.get_invoke_result(sb.ToArray())

        elif method == "invokescript":
            script = params[0].encode('utf-8')
            return self.get_invoke_result(script)

        elif method == "sendrawtransaction":
            tx_script = binascii.unhexlify(params[0].encode('utf-8'))
            transaction = Transaction.DeserializeFromBufer(tx_script)
            result = NodeLeader.Instance().Relay(transaction)
            return result

        elif method == "validateaddress":
            return self.validateaddress(params)

        elif method == "getpeers":
            return self.get_peers()

        elif method == "getbalance":
            if self.wallet:
                return self.get_balance(params)
            else:
                raise JsonRpcError(-400, "Access denied.")

        elif method == "getwalletheight":
            if self.wallet:
                return self.wallet.WalletHeight
            else:
                raise JsonRpcError(-400, "Access denied.")

        elif method == "listaddress":
            if self.wallet:
                return self.list_address()
            else:
                raise JsonRpcError(-400, "Access denied.")

        elif method == "getnewaddress":
            if self.wallet:
                keys = self.wallet.CreateKey()
                account = Account.get(
                    PublicKeyHash=keys.PublicKeyHash.ToBytes()
                )
                return account.contract_set[0].Address.ToString()
            else:
                raise JsonRpcError(-400, "Access denied.")

        elif method == "sendtoaddress":
            if self.wallet:
                contract_tx, fee = self.parse_send_to_address_params(params)
                return self.process_transaction(contract_tx=contract_tx, fee=fee)
            else:
                raise JsonRpcError(-400, "Access denied.")

        elif method == "sendfrom":
            if self.wallet:
                contract_tx, address_from, fee, change_addr = self.parse_send_from_params(params)
                return self.process_transaction(contract_tx=contract_tx, fee=fee, address_from=address_from, change_addr=change_addr)
            else:
                raise JsonRpcError(-400, "Access denied.")

        elif method == "sendmany":
            if self.wallet:
                contract_tx, fee, change_addr = self.parse_send_many_params(params)
                return self.process_transaction(contract_tx=contract_tx, fee=fee, change_addr=change_addr)
            else:
                raise JsonRpcError(-400, "Access denied.")

        elif method == "getblockheader":
            # this should work for either str or int
            blockheader = Blockchain.Default().GetHeaderBy(params[0])
            if not blockheader:
                raise JsonRpcError(-100, "Unknown block")
            return self.get_blockheader_output(blockheader, params)

        raise JsonRpcError.methodNotFound()