def test_no_combine_with_fee(self): """ Verify that unused unspents do not increase fee. """ unspents_single = [Unspent(5000, 0, '', '', 0)] unspents_original = [Unspent(5000, 0, '', '', 0), Unspent(5000, 0, '', '', 0)] outputs_original = [(RETURN_ADDRESS, 1000, 'satoshi')] unspents, outputs = sanitize_tx_data( unspents_original, outputs_original, fee=1, leftover=RETURN_ADDRESS, combine=False, message=None ) unspents_single, outputs_single = sanitize_tx_data( unspents_single, outputs_original, fee=1, leftover=RETURN_ADDRESS, combine=False, message=None ) assert unspents == [Unspent(5000, 0, '', '', 0)] assert unspents_single == [Unspent(5000, 0, '', '', 0)] assert len(outputs) == 2 assert len(outputs_single) == 2 assert outputs[1][0] == RETURN_ADDRESS assert outputs_single[1][0] == RETURN_ADDRESS assert outputs[1][1] == outputs_single[1][1]
def test_no_combine_insufficient_funds(self): unspents_original = [Unspent(1000, 0, '', '', 0), Unspent(1000, 0, '', '', 0)] outputs_original = [('test', 2500, 'satoshi')] with pytest.raises(InsufficientFunds): sanitize_tx_data( unspents_original, outputs_original, fee=50, leftover=RETURN_ADDRESS, combine=False, message=None )
def test_fee_applied(self): unspents_original = [Unspent(1000, 0, '', '', 0), Unspent(1000, 0, '', '', 0)] outputs_original = [('test', 2000, 'satoshi')] with pytest.raises(InsufficientFunds): sanitize_tx_data( unspents_original, outputs_original, fee=1, leftover=RETURN_ADDRESS, combine=True, message=None )
def prepare_transaction(cls, address, outputs, compressed=True, fee=None, leftover=None, combine=True, message=None, unspents=None): # pragma: no cover """Prepares a P2PKH transaction for offline signing. :param address: The address the funds will be sent from. :type address: ``str`` :param outputs: A sequence of outputs you wish to send in the form ``(destination, amount, currency)``. The amount can be either an int, float, or string as long as it is a valid input to ``decimal.Decimal``. The currency must be :ref:`supported <supported currencies>`. :type outputs: ``list`` of ``tuple`` :param compressed: Whether or not the ``address`` corresponds to a compressed public key. This influences the fee. :type compressed: ``bool`` :param fee: The number of satoshi per byte to pay to miners. By default Bit will poll `<https://bitcoinfees.earn.com>`_ and use a fee that will allow your transaction to be confirmed as soon as possible. :type fee: ``int`` :param leftover: The destination that will receive any change from the transaction. By default Bit will send any change to the same address you sent from. :type leftover: ``str`` :param combine: Whether or not Bit should use all available UTXOs to make future transactions smaller and therefore reduce fees. By default Bit will consolidate UTXOs. :type combine: ``bool`` :param message: A message to include in the transaction. This will be stored in the blockchain forever. Due to size limits, each message will be stored in chunks of 40 bytes. :type message: ``str`` :param unspents: The UTXOs to use as the inputs. By default Bit will communicate with the blockchain itself. :type unspents: ``list`` of :class:`~bit.network.meta.Unspent` :returns: JSON storing data required to create an offline transaction. :rtype: ``str`` """ unspents, outputs = sanitize_tx_data( unspents or NetworkAPI.get_unspent(address), outputs, fee or get_fee_cached(), leftover or address, combine=combine, message=message, compressed=compressed) data = { 'unspents': [unspent.to_dict() for unspent in unspents], 'outputs': outputs } return json.dumps(data, separators=(',', ':'))
def test_zero_remaining(self): unspents_original = [Unspent(1000, 0, '', '', 0), Unspent(1000, 0, '', '', 0)] outputs_original = [('test', 2000, 'satoshi')] unspents, outputs = sanitize_tx_data( unspents_original, outputs_original, fee=0, leftover=RETURN_ADDRESS, combine=True, message=None ) assert unspents == unspents_original assert outputs == [('test', 2000)]
def test_message(self): unspents_original = [Unspent(10000, 0, '', '', 0), Unspent(10000, 0, '', '', 0)] outputs_original = [('test', 1000, 'satoshi')] unspents, outputs = sanitize_tx_data( unspents_original, outputs_original, fee=5, leftover=RETURN_ADDRESS, combine=True, message='hello' ) assert len(outputs) == 3 assert outputs[2][0] == b'hello' assert outputs[2][1] == 0
def test_no_combine_remaining_small_inputs(self): unspents_original = [Unspent(1500, 0, '', '', 0), Unspent(1600, 0, '', '', 0), Unspent(1700, 0, '', '', 0)] outputs_original = [(RETURN_ADDRESS, 2000, 'satoshi')] unspents, outputs = sanitize_tx_data( unspents_original, outputs_original, fee=0, leftover=RETURN_ADDRESS, combine=False, message=None ) assert unspents == [Unspent(1500, 0, '', '', 0), Unspent(1600, 0, '', '', 0)] assert len(outputs) == 2 assert outputs[1][0] == RETURN_ADDRESS assert outputs[1][1] == 1100
def test_combine_remaining(self): unspents_original = [Unspent(1000, 0, '', '', 0), Unspent(1000, 0, '', '', 0)] outputs_original = [('test', 500, 'satoshi')] unspents, outputs = sanitize_tx_data( unspents_original, outputs_original, fee=0, leftover=RETURN_ADDRESS, combine=True, message=None ) assert unspents == unspents_original assert len(outputs) == 2 assert outputs[1][0] == RETURN_ADDRESS assert outputs[1][1] == 1500
def create_transaction(self, outputs, fee=None, leftover=None, combine=True, message=None, unspents=None): # pragma: no cover """Creates a signed P2PKH transaction. :param outputs: A sequence of outputs you wish to send in the form ``(destination, amount, currency)``. The amount can be either an int, float, or string as long as it is a valid input to ``decimal.Decimal``. The currency must be :ref:`supported <supported currencies>`. :type outputs: ``list`` of ``tuple`` :param fee: The number of satoshi per byte to pay to miners. By default Bit will poll `<https://bitcoinfees.earn.com>`_ and use a fee that will allow your transaction to be confirmed as soon as possible. :type fee: ``int`` :param leftover: The destination that will receive any change from the transaction. By default Bit will send any change to the same address you sent from. :type leftover: ``str`` :param combine: Whether or not Bit should use all available UTXOs to make future transactions smaller and therefore reduce fees. By default Bit will consolidate UTXOs. :type combine: ``bool`` :param message: A message to include in the transaction. This will be stored in the blockchain forever. Due to size limits, each message will be stored in chunks of 40 bytes. :type message: ``str`` :param unspents: The UTXOs to use as the inputs. By default Bit will communicate with the blockchain itself. :type unspents: ``list`` of :class:`~bit.network.meta.Unspent` :returns: The signed transaction as hex. :rtype: ``str`` """ unspents, outputs = sanitize_tx_data(unspents or self.unspents, outputs, fee or get_fee_cached(), leftover or self.address, combine=combine, message=message, compressed=self.is_compressed()) return create_p2pkh_transaction(self, unspents, outputs)
def test_no_input(self): with pytest.raises(ValueError): sanitize_tx_data([], [], 70, '')
def test_long_message(self): amount = b'\x00\x00\x00\x00\x00\x00\x00\x00' _, outputs = sanitize_tx_data( UNSPENTS, [(out[0], out[1], 'satoshi') for out in OUTPUTS], 0, RETURN_ADDRESS, message='hello'*9 ) assert construct_output_block(outputs).count(amount) == 2