diff --git a/electrum/electrumabc/consolidate.py b/electrum/electrumabc/consolidate.py --- a/electrum/electrumabc/consolidate.py +++ b/electrum/electrumabc/consolidate.py @@ -82,6 +82,8 @@ ) ] + self.sign_schnorr = wallet_instance.is_schnorr_enabled() + # Add more metadata to coins address_history = wallet_instance.get_address_history(address) received = wallet_instance.get_address_unspent(address, address_history) @@ -91,12 +93,6 @@ def get_unsigned_transactions(self) -> List[Transaction]: """ Build as many raw transactions as needed to consolidate the coins. - - :param output_address: Make all transactions send the total amount to this - address. - :param max_tx_size: Maximum tx size in bytes. This is what limits the - number of inputs per transaction. - :return: """ return list(self.iter_transactions()) @@ -112,7 +108,7 @@ """ tx_size = 0 amount = 0 - tx = Transaction(None) + tx = Transaction(None, sign_schnorr=self.sign_schnorr) tx.set_inputs([]) while tx_size < self.max_tx_size and coin_index < len(self._coins): tx_size = self.try_adding_another_coin_to_transaction( @@ -134,7 +130,7 @@ """Add coin to tx.inputs() if the resulting tx size is less than max_tx_size. Return the resulting tx_size (no matter if the coin was actually added or not). """ - dummy_tx = Transaction(None) + dummy_tx = Transaction(None, sign_schnorr=self.sign_schnorr) dummy_tx.set_inputs(tx.inputs() + [coin]) dummy_tx.set_outputs([(TYPE_ADDRESS, self.output_address, next_amount)]) tx_size = len(dummy_tx.serialize(estimate_size=True)) // 2 diff --git a/electrum/electrumabc/tests/test_consolidate.py b/electrum/electrumabc/tests/test_consolidate.py --- a/electrum/electrumabc/tests/test_consolidate.py +++ b/electrum/electrumabc/tests/test_consolidate.py @@ -36,6 +36,7 @@ tx_history.append(("a" * 64, 1)) self.mock_wallet = Mock() + self.mock_wallet.is_schnorr_enabled.return_value = False self.mock_wallet.get_addr_utxo.return_value = coins self.mock_wallet.get_address_history.return_value = tx_history self.mock_wallet.get_address_history.return_value = tx_history @@ -190,7 +191,15 @@ self.assertLessEqual(coin["height"], 700_005) self.assertEqual(len(consolidator._coins), 2) - def test_get_unsigned_transactions(self): + def test_get_unsigned_transactions_schnorr(self): + self._test_get_unsigned_transactions(sign_schnorr=True) + + def test_get_unsigned_transactions_ecdsa(self): + self._test_get_unsigned_transactions(sign_schnorr=False) + + def _test_get_unsigned_transactions(self, sign_schnorr: bool): + self.mock_wallet.is_schnorr_enabled.return_value = sign_schnorr + n_coins = 8 min_value = 1000 max_value = 1007 @@ -213,8 +222,15 @@ for tx in txs: self.assertLess(len(tx.serialize(estimate_size=True)) // 2, max_tx_size) - # tx size is roughly 148 * n_in + 34 * n_out + 10 - expected_max_n_inputs_for_size = math.floor((max_tx_size - 44) / 148) + # txid(32) + prevout_n(4) + compact_size (1) + scriptsig + sequence (4) + # with scriptsig: compact_size(1) + pubkey(33) + compact_size(1) + sig + # with sig 65 (schnorr) or ~72 (ecdsa) + input_approx_size = 148 if not sign_schnorr else 141 + # tx size is: input_size * n_in + 34 * n_out + 10 + # with n_out = 1 for consolidation transactions + expected_max_n_inputs_for_size = math.floor( + (max_tx_size - 44) / input_approx_size + ) self.assertEqual( len(txs), math.ceil(n_coins / expected_max_n_inputs_for_size) )