Esempio n. 1
0
    def next_tx(self):
        """Process the next transaction in this wallet.

        Returns:
            `TransactionType`: Whether the tx was a `SEND` or `RECEIVE`."""
        if self.tx_index == len(self.txs):
            raise StopIteration

        txn = self.txs[self.tx_index]

        self.tx_index += 1

        txid = txn['txid']
        if txn['type'] == 'received':
            amt = float_to_satoshis(txn['amount'])
            utxos = self.get_utxos(txid, amt)
            self.utxos.extend(utxos)
            return TransactionType.RECEIVE
        elif txn['type'] == 'sent':
            #remove inputs from utxos set
            inputs = self.get_inputs(txid)
            for tx_input in inputs:
                dprint(("Attempting to delete this utxo from set due to send: "
                        "%s") % str(tx_input))
                try:
                    self.utxos.remove(tx_input)
                except ValueError:
                    print(
                        ("WARNING: Missing input %s from wallet %s in tx %s. "
                         "This indicates a bug in this program, incomplete "
                         "clustering analysis, or a multi-party transaction "
                         "such as a CoinJoin. This input will be ignored.") %
                        (str(tx_input), self.wallet_label, txid))
                    continue
                dprint("Deleted this utxo from set due to send: %s" %
                       str(tx_input))

            #add change to utxo set, if any
            spend_amts = []
            for spend_output in txn['outputs']:
                assert spend_output['wallet_id'] != self.wallet_label
                spend_amt = float_to_satoshis(spend_output['amount'])
                spend_amts.append(spend_amt)
            change_utxos = self.get_utxos_mismatch_amt(txid, spend_amts)
            dprint("Adding these change utxos due to send: %s" %
                   str(change_utxos))
            self.utxos.extend(change_utxos)
            return TransactionType.SEND
        else:
            raise TypeError
Esempio n. 2
0
    def next_tx(self):
        """Process the next transaction in this wallet.

        Returns:
            `TransactionType`: Whether the tx was a `SEND` or `RECEIVE`."""
        if self.tx_index == len(self.txs):
            raise StopIteration

        txn = self.txs[self.tx_index]

        self.tx_index += 1

        txid = txn['txid']
        if txn['type'] == 'received':
            amt = float_to_satoshis(txn['amount'])
            utxos = self.get_utxos(txid, amt)
            self.utxos.extend(utxos)
            return TransactionType.RECEIVE
        elif txn['type'] == 'sent':
            #remove inputs from utxos set
            inputs = self.get_inputs(txid)
            for tx_input in inputs:
                dprint(("Attempting to delete this utxo from set due to send: "
                        "%s") % str(tx_input))
                try:
                    self.utxos.remove(tx_input)
                except ValueError:
                    print(("WARNING: Missing input %s from wallet %s in tx %s. "
                           "This indicates a bug in this program, incomplete "
                           "clustering analysis, or a multi-party transaction "
                           "such as a CoinJoin. This input will be ignored.") %
                          (str(tx_input), self.wallet_label, txid))
                    continue
                dprint("Deleted this utxo from set due to send: %s" %
                       str(tx_input))

            #add change to utxo set, if any
            spend_amts = []
            for spend_output in txn['outputs']:
                assert spend_output['wallet_id'] != self.wallet_label
                spend_amt = float_to_satoshis(spend_output['amount'])
                spend_amts.append(spend_amt)
            change_utxos = self.get_utxos_mismatch_amt(txid, spend_amts)
            dprint("Adding these change utxos due to send: %s" %
                   str(change_utxos))
            self.utxos.extend(change_utxos)
            return TransactionType.SEND
        else:
            raise TypeError
Esempio n. 3
0
    def get_current_desired_spend(self):
        """Based on the current sending transaction, get the intended spend amt.

        Returns:
            int: The total desired spend implied by this sending transaction.
        """
        current_tx = self.txs[self.tx_index]
        if current_tx['type'] != 'sent':
            dprint(str(current_tx))
            raise CurrentTxNotSendError
        desired_spend = 0
        for output in current_tx['outputs']:
            desired_spend += float_to_satoshis(output['amount'])
        return desired_spend
Esempio n. 4
0
    def get_current_desired_spend(self):
        """Based on the current sending transaction, get the intended spend amt.

        Returns:
            int: The total desired spend implied by this sending transaction.
        """
        current_tx = self.txs[self.tx_index]
        if current_tx['type'] != 'sent':
            dprint(str(current_tx))
            raise CurrentTxNotSendError
        desired_spend = 0
        for output in current_tx['outputs']:
            desired_spend += float_to_satoshis(output['amount'])
        return desired_spend
Esempio n. 5
0
    def get_utxos(self, txid, output_amt):
        """Find outputs for specified tx sent to this wallet.

        WalletExplorer.com's /wallet endpoint indicates a total amount sent
        to the wallet but not specifically which output(s). We'll first try
        to use bitcoind's RPC interface to match the amount to a unique
        transaction output. If that can't be done, we'll follow up with a call
        to the /tx API endpoint and get a complete list of outputs sent to the
        wallet.

        Args:
            txid (str): Transaction hash.
            output_amt (int): The amount of the output we want to find in
                integer satoshi terms. In the event of duplicate values in a
                transaction's outputs, the first one is used.

        Returns:
            List[utxos], with each utxo as a tuple of
                (txid, output_index, amt_satoshis).
        """
        dprint("Looking for output amt %s in %s" % (str(output_amt), txid))
        assert type(txid) in (str, unicode)
        assert isinstance(output_amt, int) or isinstance(output_amt, long)

        tx_rpc_json = self.conn.get_decoded_tx(txid)
        utxo = None
        for i, vout in enumerate(tx_rpc_json['vout']):
            if float_to_satoshis(vout['value']) == output_amt:
                if utxo is None:
                    utxo = (txid, i, output_amt)
                else:
                    #resolve ambiguity as to which output is correct
                    utxos = http.get_outputs_sent_to_wallet(txid,
                                                            self.wallet_label)
                    return utxos
        if utxo is None:
            #there may be multiple outputs that add up the specified amount
            utxos = http.get_outputs_sent_to_wallet(txid, self.wallet_label)
            utxo_sum = 0
            for utxo in utxos:
                utxo_sum += utxo[2]
            if utxo_sum != output_amt:
                raise OutputNotFoundError
            else:
                return utxos
        else:
            return [utxo]
Esempio n. 6
0
    def get_utxos(self, txid, output_amt):
        """Find outputs for specified tx sent to this wallet.

        WalletExplorer.com's /wallet endpoint indicates a total amount sent
        to the wallet but not specifically which output(s). We'll first try
        to use bitcoind's RPC interface to match the amount to a unique
        transaction output. If that can't be done, we'll follow up with a call
        to the /tx API endpoint and get a complete list of outputs sent to the
        wallet.

        Args:
            txid (str): Transaction hash.
            output_amt (int): The amount of the output we want to find in
                integer satoshi terms. In the event of duplicate values in a
                transaction's outputs, the first one is used.

        Returns:
            List[utxos], with each utxo as a tuple of
                (txid, output_index, amt_satoshis).
        """
        dprint("Looking for output amt %s in %s" % (str(output_amt), txid))
        assert type(txid) in (str, unicode)
        assert isinstance(output_amt, int) or isinstance(output_amt, long)

        tx_rpc_json = self.conn.get_decoded_tx(txid)
        utxo = None
        for i, vout in enumerate(tx_rpc_json['vout']):
            if float_to_satoshis(vout['value']) == output_amt:
                if utxo is None:
                    utxo = (txid, i, output_amt)
                else:
                    #resolve ambiguity as to which output is correct
                    utxos = http.get_outputs_sent_to_wallet(
                        txid, self.wallet_label)
                    return utxos
        if utxo is None:
            #there may be multiple outputs that add up the specified amount
            utxos = http.get_outputs_sent_to_wallet(txid, self.wallet_label)
            utxo_sum = 0
            for utxo in utxos:
                utxo_sum += utxo[2]
            if utxo_sum != output_amt:
                raise OutputNotFoundError
            else:
                return utxos
        else:
            return [utxo]
Esempio n. 7
0
    def get_inputs(self, txid):
        """Get a list of inputs for the specified transaction.

        Returns:
            List of utxos as tuples of (txid, output_index, amt_in_satoshis).
        """
        tx_rpc_json = self.conn.get_decoded_tx(txid)
        inputs = []
        for vin in tx_rpc_json['vin']:
            input_prev_txid = vin['txid']
            input_prev_index = vin['vout']
            input_rpc_json = self.conn.get_decoded_tx(input_prev_txid)
            input_amt = input_rpc_json['vout'][input_prev_index]['value']
            input_satoshis = float_to_satoshis(input_amt)
            utxo = (input_prev_txid,input_prev_index, input_satoshis)
            inputs.append(utxo)
        return inputs
Esempio n. 8
0
    def get_inputs(self, txid):
        """Get a list of inputs for the specified transaction.

        Returns:
            List of utxos as tuples of (txid, output_index, amt_in_satoshis).
        """
        tx_rpc_json = self.conn.get_decoded_tx(txid)
        inputs = []
        for vin in tx_rpc_json['vin']:
            input_prev_txid = vin['txid']
            input_prev_index = vin['vout']
            input_rpc_json = self.conn.get_decoded_tx(input_prev_txid)
            input_amt = input_rpc_json['vout'][input_prev_index]['value']
            input_satoshis = float_to_satoshis(input_amt)
            utxo = (input_prev_txid, input_prev_index, input_satoshis)
            inputs.append(utxo)
        return inputs
Esempio n. 9
0
def get_outputs_sent_to_wallet(txid, wallet_label):
    """Find all outputs in tx sent to specified wallet using remote API.

    Returns:
        List[utxos], with each tuple as (txid, output_index, amt_satoshis)
    """
    tx_json = get_tx_data_json(txid)
    utxos = []
    for i, output in enumerate(tx_json['out']):
        receiver_wallet = None
        amt_in_satoshis = float_to_satoshis(output['amount'])
        if 'label' in output:
            receiver_wallet = output['label']
        else:
            receiver_wallet = output['wallet_id']
        if receiver_wallet == wallet_label:
            utxo = (txid, i, amt_in_satoshis)
            utxos.append(utxo)
    return utxos
Esempio n. 10
0
    def get_utxos_mismatch_amt(self, txid, amt_list):
        """Return all utxos that don't match the output amounts specified.

        Args:
            txid (str): transaction hash
            amt_list (List[int]): List of amounts we want to omit, each
                expressed in integer satoshi terms.
        Returns:
            List of utxos as tuples of (txid, output_index, amt_in_satoshis).
        """
        assert type(txid) in (str, unicode)
        assert isinstance(amt_list, list)

        tx_rpc_json = self.conn.get_decoded_tx(txid)
        utxos = []
        for i, vout in enumerate(tx_rpc_json['vout']):
            output_val = float_to_satoshis(vout['value'])
            if output_val not in amt_list:
                utxo = (txid, i, output_val)
                utxos.append(utxo)
        return utxos
Esempio n. 11
0
    def get_utxos_mismatch_amt(self, txid, amt_list):
        """Return all utxos that don't match the output amounts specified.

        Args:
            txid (str): transaction hash
            amt_list (List[int]): List of amounts we want to omit, each
                expressed in integer satoshi terms.
        Returns:
            List of utxos as tuples of (txid, output_index, amt_in_satoshis).
        """
        assert type(txid) in (str, unicode)
        assert isinstance(amt_list, list)

        tx_rpc_json = self.conn.get_decoded_tx(txid)
        utxos = []
        for i, vout in enumerate(tx_rpc_json['vout']):
            output_val = float_to_satoshis(vout['value'])
            if output_val not in amt_list:
                utxo = (txid, i, output_val)
                utxos.append(utxo)
        return utxos