예제 #1
0
    def extract_secret_from_redeem_transaction(
            cls, contract_address: str) -> Optional[str]:
        '''
        Extracting secret from redeem transaction based on contract address.

        Args:
            contract_address (str): address of the contract atomic swap contract

        Returns:
            str, None: Secret string or None if contract wasn't redeemed yet.

        Example:
            >>> from clove.network import BitcoinTestNet
            >>> network = BitcoinTestNet()
            >>> network.extract_secret_from_redeem_transaction('2N7Gxryn4dD1mdyGM3DMxMAwD7k3RBTJ1gP')
            90f6b9b9a34acb486654b3e9cdc02cce0b8e40a8845924ffda68453ac2477d20
        '''
        contract_transactions = clove_req_json(
            f'{cls.api_url}/addr/{contract_address}')['transactions']
        if not contract_transactions:
            logger.error(
                f'Cannot get contract transactions ({cls.symbols[0]})')
            return
        if len(contract_transactions) < 2:
            logger.debug(
                'There is no redeem transaction on this contract yet.')
            return
        redeem_transaction = cls.get_transaction(contract_transactions[0])
        if not redeem_transaction:
            logger.error(f'Cannot get redeem transaction ({cls.symbols[0]})')
            return
        return cls.extract_secret(
            scriptsig=redeem_transaction['vin'][0]['scriptSig']['hex'])
예제 #2
0
    def extract_secret_from_redeem_transaction(
            cls, contract_address: str) -> Optional[str]:
        '''
        Extracting secret from redeem transaction based on contract address.

        Args:
            contract_address (str): address of the contract atomic swap contract

        Returns:
            str, None: Secret string or None if contract wasn't redeemed yet.

        Example:
            >>> from clove.network import BitcoinTestNet
            >>> network = BitcoinTestNet()
            >>> network.extract_secret_from_redeem_transaction('2N7Gxryn4dD1mdyGM3DMxMAwD7k3RBTJ1gP')
            90f6b9b9a34acb486654b3e9cdc02cce0b8e40a8845924ffda68453ac2477d20
        '''
        data = clove_req_json(
            f'{cls.blockcypher_url()}/addrs/{contract_address}/full')
        if not data:
            logger.error('Unexpected response from blockcypher')
            raise ValueError('Unexpected response from blockcypher')

        transactions = data['txs']
        if len(transactions) == 1:
            logger.debug('Contract was not redeemed yet.')
            return

        return cls.extract_secret(
            scriptsig=transactions[0]['inputs'][0]['script'])
예제 #3
0
    def get_utxo(cls, address: str, amount: float):
        data = clove_req_json(
            f'{cls.blockcypher_url()}/addrs/{address}'
            '?limit=2000&unspentOnly=true&includeScript=true&confirmations=6')
        unspent = data.get('txrefs', [])

        for output in unspent:
            output['value'] = int(output['value'])

        unspent = sorted(unspent, key=lambda k: k['value'], reverse=True)

        utxo = []
        total = 0

        for output in unspent:
            value = from_base_units(output['value'])
            utxo.append(
                Utxo(
                    tx_id=output['tx_hash'],
                    vout=output['tx_output_n'],
                    value=value,
                    tx_script=output['script'],
                ))
            total += value
            if total > amount:
                return utxo

        logger.debug(f'Cannot find enough UTXO\'s. Found %.8f from %.8f.',
                     total, amount)
예제 #4
0
파일: cryptoid.py 프로젝트: MerlinB/clove
    def get_utxo(cls, address: str, amount: float):
        api_key = os.environ.get('CRYPTOID_API_KEY')
        if not api_key:
            raise ValueError('API key for cryptoid is required to get UTXOs.')
        data = clove_req_json(f'{cls.cryptoid_url()}/api.dws?q=unspent&key={api_key}&active={address}')
        unspent = data.get('unspent_outputs', [])

        for output in unspent:
            output['value'] = int(output['value'])

        unspent = sorted(unspent, key=lambda k: k['value'], reverse=True)

        utxo = []
        total = 0

        for output in unspent:
            value = from_base_units(output['value'])
            utxo.append(
                Utxo(
                    tx_id=output['tx_hash'],
                    vout=output['tx_ouput_n'],
                    value=value,
                    tx_script=output['script'],
                )
            )
            total += value
            if total > amount:
                return utxo

        logger.debug(f'Cannot find enough UTXO\'s. Found %.8f from %.8f.', total, amount)
예제 #5
0
    def get_latest_block(cls) -> Optional[int]:
        '''
        Returns the number of the latest block.

        Returns:
            int, None: number of the latest block or None if API is not working

        Example:
            >>> from clove.network import Bitcoin
            >>> network = Bitcoin()
            >>> network.get_latest_block()
            544989
        '''
        try:
            latest_block = clove_req_json(
                f'{cls.api_url}/status?q=getInfo')['info']['blocks']
        except (TypeError, KeyError):
            logger.error(
                f'Cannot get latest block, bad response ({cls.symbols[0]})')
            return
        if not latest_block:
            logger.debug(f'Latest block not found ({cls.symbols[0]})')
            return
        logger.debug(f'Latest block found: {latest_block}')
        return latest_block
예제 #6
0
    def get_fee(cls) -> Optional[float]:
        '''
        Getting actual fee per kb

        Returns:
            float, None: actual fee per kb or None if eg. API is not responding

        Example:
            >>> from clove.network import BitcoinTestNet
            >>> network = BitcoinTestNet()
            >>> network.get_fee()
            0.00024538
        '''
        try:
            # This endpoint is available from v0.3.1
            fee = clove_req_json(
                f'{cls.api_url}/utils/estimatefee?nbBlocks=1')['1']
        except (TypeError, KeyError):
            logger.error(
                f'Incorrect response from API when getting fee from {cls.api_url}/utils/estimatefee?nbBlocks=1'
            )
            return cls._calculate_fee()

        if fee == -1:
            logger.debug(f'Incorrect value in estimatedFee: {fee}')
            return cls._calculate_fee()
        fee = float(fee)
        if fee > 0:
            logger.warning(
                f'Got fee = 0 for ({cls.symbols[0]}), calculating manually')
            return fee
        return cls._calculate_fee()
예제 #7
0
파일: graphql.py 프로젝트: MerlinB/clove
    def extract_secret_from_redeem_transaction(
            cls, contract_address: str) -> Optional[str]:

        query = """
        {
            allAddressTxes(orderBy: TIME_ASC, condition: { address: "%s" }) {
                nodes {
                    txId
                }
            }
        }
        """ % (contract_address)

        data = clove_req_json(f'{cls.api_url}/graphql',
                              post_data={'query': query})
        contract_transactions = data['data']['allAddressTxes']['nodes']

        if not contract_transactions:
            logger.error(
                f'Cannot get contract transactions ({cls.symbols[0]})')
            return
        if len(contract_transactions) < 2:
            logger.debug(
                'There is no redeem transaction on this contract yet.')
            return

        redeem_transaction = cls.get_transaction(
            contract_transactions[1]['txId'])
        if not redeem_transaction:
            logger.error(f'Cannot get redeem transaction ({cls.symbols[0]})')
            return

        return cls.extract_secret(scriptsig=redeem_transaction['vinsByTxId']
                                  ['nodes'][0]['scriptSig'])
예제 #8
0
파일: graphql.py 프로젝트: MerlinB/clove
    def get_latest_block(cls) -> Optional[int]:
        '''Returns the number of the latest block.'''

        query = '''
        {
            allBlocks(orderBy: HEIGHT_DESC, first: 1) {
                nodes {
                    height
                }
            }
        }
        '''

        try:
            json_response = clove_req_json(f'{cls.api_url}/graphql',
                                           post_data={'query': query})
            latest_block = json_response['data']['allBlocks']['nodes'][0][
                'height']
        except (TypeError, KeyError):
            logger.error(
                f'Cannot get latest block, bad response ({cls.symbols[0]})')
            return
        if not latest_block:
            logger.debug(f'Latest block not found ({cls.symbols[0]})')
            return
        logger.debug(f'Latest block found: {latest_block}')
        return latest_block
예제 #9
0
 def get_balance(cls, wallet_address: str) -> Optional[float]:
     data = clove_req_json(
         f'{cls.blockcypher_url()}/addrs/{wallet_address}/balance')
     if data is None:
         logger.error('Could not get details for address %s in %s network',
                      wallet_address, cls.symbols[0])
         return
     return from_base_units(data['balance'] or data['unconfirmed_balance'])
예제 #10
0
 def _get_block_hash(cls, block_number: int) -> str:
     '''Getting block hash by its number'''
     try:
         block_hash = clove_req_json(f'{cls.api_url}/block-index/{block_number}')['blockHash']
     except (TypeError, KeyError):
         logger.error(f'Cannot get block hash for block {block_number} ({cls.symbols[0]})')
         return
     logger.debug(f'Found hash for block {block_number}: {block_hash}')
     return block_hash
예제 #11
0
파일: cryptoid.py 프로젝트: MerlinB/clove
 def get_balance(cls, wallet_address: str) -> float:
     api_key = os.environ.get('CRYPTOID_API_KEY')
     if api_key is None:
         raise ValueError('API key for cryptoid is required to get balance.')
     data = clove_req_json(f'{cls.cryptoid_url()}/api.dws?q=getbalance&a={wallet_address}&key={api_key}')
     if data is None:
         logger.debug('Could not get details for address %s in %s network', wallet_address, cls.symbols[0])
         return
     return data
예제 #12
0
 def get_fee(cls) -> Optional[float]:
     '''Returns actual fee per kb.'''
     response = clove_req_json(cls.fee_endpoint)
     fee = response.get('high_fee_per_kb')
     if not fee:
         logger.error(
             'Cannot find the right key (high_fee_per_kb) while getting fee in blockcypher.'
         )
         return
     return from_base_units(fee)
예제 #13
0
 def extract_secret_from_redeem_transaction(
         cls, contract_address: str) -> Optional[str]:
     contract_transactions = clove_req_json(
         f'https://mona.chainseeker.info/api/v1/txids/{contract_address}')
     if len(contract_transactions) < 2:
         logger.debug(
             'There is no redeem transaction on this contract yet.')
         return
     redeem_transaction = cls.get_transaction(contract_transactions[1])
     return cls.extract_secret(redeem_transaction['hex'])
예제 #14
0
 def _get_transactions_from_block(cls, block_number: int):
     '''Getting transactions from block by given block number'''
     block_hash = cls._get_block_hash(block_number)
     if not block_hash:
         return
     transactions_page = clove_req_json(f'{cls.api_url}/txs/?block={block_hash}')
     if not transactions_page:
         return
     transactions = transactions_page['txs']
     logger.debug(f'Found {len(transactions)} in block {block_number}')
     return transactions
예제 #15
0
 def get_latest_block(cls) -> Optional[int]:
     '''Returns the number of the latest block.'''
     try:
         latest_block = clove_req_json(f'{cls.api_url}/status?q=getInfo')['info']['blocks']
     except (TypeError, KeyError):
         logger.error(f'Cannot get latest block, bad response ({cls.symbols[0]})')
         return
     if not latest_block:
         logger.debug(f'Latest block not found ({cls.symbols[0]})')
         return
     logger.debug(f'Latest block found: {latest_block}')
     return latest_block
예제 #16
0
    def get_utxo(cls, address: str, amount: float) -> Optional[list]:
        '''
        Getting list of UTXO objects.

        Args:
            address (str): wallet address to look for UTXO
            amount (float): minimum value that should be satisfied in UTXO objects

        Returns:
            list, None: list of UTXO objects or None it there was not enough UTXO

        Example:
            >>> from clove.network import Litecoin
            >>> network = Litecoin()
            >>> network.get_utxo(address='LUAn5PWmsPavgz32mGkqsUuAKncftS37Jq', amount=0.01)
            [
             Utxo(tx_id='0cd90567497823097d03464b4b2d08dd659f1c5621dd55e9540bc9bcd3e191ec', vout='0', value='0.00976168', tx_script='76a91485c0522f6e23beb11cc3d066cd20ed732648a4e688ac', wallet=None, secret=None, refund=False),  # noqa: E501
             Utxo(tx_id='a5c027027c695f403fe570850e35ffd44bb28479ecaaee039372015fe0aae7b2', vout='0', value='0.00097114', tx_script='76a91485c0522f6e23beb11cc3d066cd20ed732648a4e688ac', wallet=None, secret=None, refund=False)  # noqa: E501
            ]
        '''
        data = clove_req_json(
            f'{cls.blockcypher_url()}/addrs/{address}'
            '?limit=2000&unspentOnly=true&includeScript=true&confirmations=6')
        if not data:
            logger.debug(
                f'Cannot find UTXO for address {address} ({cls.symbols[0]})')
            return
        unspent = data.get('txrefs', [])

        for output in unspent:
            output['value'] = int(output['value'])

        unspent = sorted(unspent, key=lambda k: k['value'], reverse=True)

        utxo = []
        total = 0

        for output in unspent:
            value = from_base_units(output['value'])
            utxo.append(
                Utxo(
                    tx_id=output['tx_hash'],
                    vout=output['tx_output_n'],
                    value=value,
                    tx_script=output['script'],
                ))
            total += value
            if total > amount:
                return utxo

        logger.debug(f'Cannot find enough UTXO\'s. Found %.8f from %.8f.',
                     total, amount)
예제 #17
0
 def extract_secret_from_redeem_transaction(cls, contract_address: str) -> Optional[str]:
     contract_transactions = clove_req_json(f'{cls.api_url}/txids/{contract_address}')
     if not contract_transactions:
         logger.error(f'Cannot get contract transactions ({cls.symbols[0]})')
         return
     if len(contract_transactions) < 2:
         logger.debug('There is no redeem transaction on this contract yet.')
         return
     redeem_transaction = cls.get_transaction(contract_transactions[1])
     if not redeem_transaction:
         logger.error(f'Cannot get redeem transaction ({cls.symbols[0]})')
         return
     return cls.extract_secret(redeem_transaction['hex'])
예제 #18
0
    def get_current_fee_per_kb(cls) -> Optional[float]:
        """Getting current network fee from Clove API"""

        network = cls.symbols[0].upper()
        if cls.testnet:
            network += '-TESTNET'
        resp = clove_req_json(f'{CLOVE_API_URL}/fee/{network}')

        if not resp:
            logger.debug('Could not get current fee for %s network', network)
            return

        return resp['fee']
예제 #19
0
 def get_fee(cls) -> float:
     """Ravencoin has a different endpoint for fee (estimatesmartfee, not estimatefee)"""
     try:
         fee = clove_req_json(f'{cls.api_url}/utils/estimatesmartfee?nbBlocks=1')['1']
     except (TypeError, KeyError):
         logger.error(
             f'Incorrect response from API when getting fee from {cls.api_url}/utils/estimatefee?nbBlocks=1'
         )
         return cls._calculate_fee()
     if fee > 0:
         return fee
     logger.warning(f'({cls.symbols[0]}) Got fee = 0, calculating manually')
     return cls._calculate_fee()
예제 #20
0
파일: cryptoid.py 프로젝트: MerlinB/clove
    def extract_secret_from_redeem_transaction(cls, contract_address: str) -> Optional[str]:
        api_key = os.environ.get('CRYPTOID_API_KEY')
        if not api_key:
            raise ValueError('API key for cryptoid is required.')

        data = clove_req_json(f'{cls.cryptoid_url()}/api.dws?q=multiaddr&active={contract_address}&key={api_key}')
        if not data:
            logger.debug('Unexpected response from cryptoid')
            raise ValueError('Unexpected response from cryptoid')

        transactions = data['txs']
        if len(transactions) == 1:
            logger.debug('Contract was not redeemed yet.')
            return

        redeem_tx_hash = transactions[0]['hash']
        logger.warning('Using undocumented endpoint used by chainz.cryptoid.info site.')
        data = clove_req_json(f'{cls.api_url}/explorer/tx.raw.dws?coin={cls.symbols[0].lower()}&id={redeem_tx_hash}')
        if not data:
            logger.debug('Unexpected response from cryptoid')
            raise ValueError('Unexpected response from cryptoid')

        return cls.extract_secret(scriptsig=data['vin'][0]['scriptSig']['hex'])
예제 #21
0
    def get_latest_block(cls) -> Optional[int]:
        '''
        Returns the number of the latest block.

        Returns:
            int, None: number of the latest block or None if API is not working

        Example:
            >>> from clove.network import Bitcoin
            >>> network = Bitcoin()
            >>> network.get_latest_block()
            544989
        '''
        return clove_req_json(f'{cls.blockcypher_url()}')['height']
예제 #22
0
    def get_transaction(cls, tx_address: str) -> Optional[dict]:
        '''
        Getting transaction details.

        Args:
            tx_address (str): transaction address

        Returns:
            dict, None: dictionary with transaction details or None if transaction doesn't exist

        Example:
            >>> from clove.network import Bitcoin
            >>> network = Bitcoin()
            >>> network.get_transaction('a82213fd237a2b4bf05c805611dc913125aef138bf2874f0668133a4bb5f3b64')
            {'blockhash': '0000000000000000000e2d8d964b4da69f2f30b79aaa58597848719bf0b86a1b',
             'blockheight': 544987,
             'blocktime': 1539068621,
             'confirmations': 3,
             'fees': 0.0011,
             'locktime': 0,
             'size': 192,
             'time': 1539068621,
             'txid': 'a82213fd237a2b4bf05c805611dc913125aef138bf2874f0668133a4bb5f3b64',
             'valueIn': 0.60725408,
             'valueOut': 0.60615408,
             'version': 1,
             'vin': [{'addr': '163o7ig87TnUnp1QF1rBrsjU1uhfEW9nNU',
               'doubleSpentTxID': None,
               'n': 0,
               'scriptSig': {
                'asm': '3045022100ad5db8c05d7f702c8328ae5a817a13dd7f0fda876e3bb3b7729b041bb612275502200af30b833c06c8485ccec95de48c2238a4ffa4e4820dd6466b95dc5d26e883ae[ALL] 03b504de54d5940a81cf5f8c483025c6f39bfc7eed60a022549513fd8d6e41d74f',  # noqa: E50
                'hex': '483045022100ad5db8c05d7f702c8328ae5a817a13dd7f0fda876e3bb3b7729b041bb612275502200af30b833c06c8485ccec95de48c2238a4ffa4e4820dd6466b95dc5d26e883ae012103b504de54d5940a81cf5f8c483025c6f39bfc7eed60a022549513fd8d6e41d74f'},  # noqa: E501
               'sequence': 4294967295,
               'txid': '101cc115cc6882e1fd150c35efd056d18a73c12a3321c406960844561922dfc0',
               'value': 0.60725408,
               'valueSat': 60725408,
               'vout': 0}],
             'vout': [{'n': 0,
               'scriptPubKey': {'addresses': ['13xMGnw7sTTVXT26YpfZQkk2rvuvJFoMvi'],
                'asm': 'OP_DUP OP_HASH160 20680d49e72e1de6af9a0180b3293f2cbd0d0666 OP_EQUALVERIFY OP_CHECKSIG',
                'hex': '76a91420680d49e72e1de6af9a0180b3293f2cbd0d066688ac',
                'type': 'pubkeyhash'},
               'spentHeight': None,
               'spentIndex': None,
               'spentTxId': None,
               'value': '0.60615408'}]}
        '''
        return clove_req_json(
            f'{cls.blockcypher_url()}/txs/{tx_address}?includeHex=true')
예제 #23
0
    def extract_secret_from_redeem_transaction(
            cls, contract_address: str) -> Optional[str]:
        data = clove_req_json(
            f'{cls.blockcypher_url()}/addrs/{contract_address}/full')
        if not data:
            logger.error('Unexpected response from blockcypher')
            raise ValueError('Unexpected response from blockcypher')

        transactions = data['txs']
        if len(transactions) == 1:
            logger.debug('Contract was not redeemed yet.')
            return

        return cls.extract_secret(
            scriptsig=transactions[0]['inputs'][0]['script'])
예제 #24
0
파일: graphql.py 프로젝트: MerlinB/clove
    def get_utxo(cls, address, amount):
        query = """
        {
            getAddressTxs(_address: "%s") {
                nodes {
                    voutsByTxId(condition: { spendingN: null }) {
                        nodes {
                            txId
                            n
                            value
                            scriptPubKey
                        }
                    }
                }
            }
        }
        """ % (address)

        data = clove_req_json(f'{cls.api_url}/graphql',
                              post_data={'query': query})
        vouts = []
        for node in data['data']['getAddressTxs']['nodes']:
            for vout in node['voutsByTxId']['nodes']:
                vouts.append(vout)

        unspent = sorted(vouts, key=lambda k: k['value'], reverse=True)

        utxo = []
        total = 0

        for output in unspent:
            value = float(output['value'])
            utxo.append(
                Utxo(
                    tx_id=output['txId'],
                    vout=output['n'],
                    value=value,
                    tx_script=output['scriptPubKey'],
                ))
            total += value
            if total > amount:
                return utxo

        logger.debug(f'Cannot find enough UTXO\'s. Found %.8f from %.8f.',
                     total, amount)
예제 #25
0
    def get_fee(cls) -> Optional[float]:
        # This endpoint is available from v0.3.1
        try:
            fee = clove_req_json(f'{cls.api_url}/utils/estimatefee?nbBlocks=1')['1']
        except (TypeError, KeyError):
            logger.error(
                f'Incorrect response from API when getting fee from {cls.api_url}/utils/estimatefee?nbBlocks=1'
            )
            return cls._calculate_fee()

        if fee == -1:
            logger.debug(f'Incorrect value in estimatedFee: {fee}')
            return cls._calculate_fee()
        fee = float(fee)
        if fee > 0:
            logger.warning(f'Got fee = 0 for ({cls.symbols[0]}), calculating manually')
            return fee
        return cls._calculate_fee()
예제 #26
0
    def find_redeem_transaction(self, recipient_address: str, contract_address: str, value: int) -> Optional[str]:
        recipient_address = recipient_address.lower()
        contract_address = contract_address.lower()
        value = str(value)

        etherscan_api_key = os.getenv('ETHERSCAN_API_KEY')
        if not etherscan_api_key:
            raise ValueError('API key for etherscan is required.')

        data = clove_req_json(
            f'http://{self.etherscan_api_subdomain}.etherscan.io/api?module=account&action=txlistinternal'
            f'&address={recipient_address}&apikey={etherscan_api_key}'
        )

        for result in reversed(data['result']):
            if result['to'] == recipient_address and result['from'] == contract_address and result['value'] == value:
                return result['hash']

        logger.debug('Redeem transaction not found.')
예제 #27
0
    def get_balance(cls, wallet_address: str) -> float:
        '''
        Returns wallet balance without unconfirmed transactions.

        Args:
            wallet_address (str): wallet address

        Returns:
            float: amount converted from base units

        Example:
            >>> from clove.network import Ravencoin
            >>> r = Ravencoin()
            >>> r.get_balance('RM7w75BcC21LzxRe62jy8JhFYykRedqu8k')
            >>> 18.99
        '''
        wallet_utxo = clove_req_json(f'{cls.api_url}/addr/{wallet_address}/balance')
        if not wallet_utxo:
            return 0
        return from_base_units(wallet_utxo)
예제 #28
0
    def get_fee(cls) -> Optional[float]:
        '''
        Getting actual fee per kb

        Returns:
            float, None: actual fee per kb or None if eg. API is not responding

        Example:
            >>> from clove.network import BitcoinTestNet
            >>> network = BitcoinTestNet()
            >>> network.get_fee()
            0.00024538
        '''
        response = clove_req_json(cls.blockcypher_url())
        fee = response.get('high_fee_per_kb')
        if not fee:
            logger.error(
                'Cannot find the right key (high_fee_per_kb) while getting fee in blockcypher.'
            )
            return
        return from_base_units(fee)
예제 #29
0
파일: graphql.py 프로젝트: MerlinB/clove
    def get_balance(cls, wallet_address: str) -> float:
        '''
        Returns wallet balance without unconfirmed transactions.

        Args:
            wallet_address (str): wallet address

        Returns:
            float: amount converted from base units

        Example:
            >>> from clove.network import Ravencoin
            >>> r = Ravencoin()
            >>> r.get_balance('RM7w75BcC21LzxRe62jy8JhFYykRedqu8k')
            >>> 18.99
        '''

        query = """
        {
            getAddressTxs(_address: "%s") {
                nodes {
                    voutsByTxId(condition: { spendingN: null }) {
                        nodes {
                            value
                        }
                    }
                }
            }
        }
        """ % (wallet_address)

        data = clove_req_json(f'{cls.api_url}/graphql',
                              post_data={'query': query})
        total = 0
        for node in data['data']['getAddressTxs']['nodes']:
            for vout in node['voutsByTxId']['nodes']:
                total += float(vout['value'])

        return total
예제 #30
0
    def get_utxo(cls, address, amount):
        data = clove_req_json(f'{cls.api_url}/addrs/{address}/utxo')
        unspent = sorted(data, key=lambda k: k['satoshis'], reverse=True)

        utxo = []
        total = 0

        for output in unspent:
            value = from_base_units(output['satoshis'])
            utxo.append(
                Utxo(
                    tx_id=output['txid'],
                    vout=output['vout'],
                    value=value,
                    tx_script=output['scriptPubKey'],
                )
            )
            total += value
            if total > amount:
                return utxo

        logger.debug(f'Cannot find enough UTXO\'s. Found %.8f from %.8f.', total, amount)