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))
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)
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))
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)
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))
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
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)
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
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.')
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
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
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()