tMerge pull request #4596 from SomberNight/txoutput_namedtuple - electrum - Electrum Bitcoin wallet
HTML git clone https://git.parazyd.org/electrum
DIR Log
DIR Files
DIR Refs
DIR Submodules
---
DIR commit 194ee395e70ad44d19732acd69fe8eec2693f968
DIR parent f64062b6f13dfc340fedcdfdf24156ae21414c1c
HTML Author: ThomasV <thomasv@electrum.org>
Date: Thu, 2 Aug 2018 12:49:51 +0200
Merge pull request #4596 from SomberNight/txoutput_namedtuple
ttransaction: introduce TxOutput namedtuple
Diffstat:
M electrum/address_synchronizer.py | 13 ++++++-------
M electrum/coinchooser.py | 4 ++--
M electrum/commands.py | 6 +++---
M electrum/gui/kivy/main_window.py | 3 ++-
M electrum/gui/kivy/uix/dialogs/__in… | 6 +++---
M electrum/gui/kivy/uix/screens.py | 3 ++-
M electrum/gui/qt/main_window.py | 12 ++++++------
M electrum/gui/qt/paytoedit.py | 13 +++++++------
M electrum/gui/stdio.py | 4 +++-
M electrum/gui/text.py | 4 +++-
M electrum/paymentrequest.py | 7 ++++---
M electrum/plugins/digitalbitbox/dig… | 6 +++---
M electrum/plugins/hw_wallet/plugin.… | 12 ++++++------
M electrum/plugins/keepkey/keepkey.py | 5 +++--
M electrum/plugins/ledger/ledger.py | 10 +++++-----
M electrum/plugins/safe_t/safe_t.py | 5 +++--
M electrum/plugins/trezor/trezor.py | 5 +++--
M electrum/plugins/trustedcoin/trust… | 9 +++++----
M electrum/tests/test_wallet_vertica… | 45 ++++++++++++++++---------------
M electrum/transaction.py | 26 +++++++++++++++-----------
M electrum/wallet.py | 33 ++++++++++++++-----------------
21 files changed, 122 insertions(+), 109 deletions(-)
---
DIR diff --git a/electrum/address_synchronizer.py b/electrum/address_synchronizer.py
t@@ -28,7 +28,7 @@ from collections import defaultdict
from . import bitcoin
from .bitcoin import COINBASE_MATURITY, TYPE_ADDRESS, TYPE_PUBKEY
from .util import PrintError, profiler, bfh, VerifiedTxInfo, TxMinedStatus
-from .transaction import Transaction
+from .transaction import Transaction, TxOutput
from .synchronizer import Synchronizer
from .verifier import SPV
from .blockchain import hash_header
t@@ -112,12 +112,11 @@ class AddressSynchronizer(PrintError):
return addr
return None
- def get_txout_address(self, txo):
- _type, x, v = txo
- if _type == TYPE_ADDRESS:
- addr = x
- elif _type == TYPE_PUBKEY:
- addr = bitcoin.public_key_to_p2pkh(bfh(x))
+ def get_txout_address(self, txo: TxOutput):
+ if txo.type == TYPE_ADDRESS:
+ addr = txo.address
+ elif txo.type == TYPE_PUBKEY:
+ addr = bitcoin.public_key_to_p2pkh(bfh(txo.address))
else:
addr = None
return addr
DIR diff --git a/electrum/coinchooser.py b/electrum/coinchooser.py
t@@ -26,7 +26,7 @@ from collections import defaultdict, namedtuple
from math import floor, log10
from .bitcoin import sha256, COIN, TYPE_ADDRESS, is_address
-from .transaction import Transaction
+from .transaction import Transaction, TxOutput
from .util import NotEnoughFunds, PrintError
t@@ -178,7 +178,7 @@ class CoinChooserBase(PrintError):
# size of the change output, add it to the transaction.
dust = sum(amount for amount in amounts if amount < dust_threshold)
amounts = [amount for amount in amounts if amount >= dust_threshold]
- change = [(TYPE_ADDRESS, addr, amount)
+ change = [TxOutput(TYPE_ADDRESS, addr, amount)
for addr, amount in zip(change_addrs, amounts)]
self.print_error('change:', change)
if dust:
DIR diff --git a/electrum/commands.py b/electrum/commands.py
t@@ -38,7 +38,7 @@ from .util import bfh, bh2u, format_satoshis, json_decode, print_error, json_enc
from . import bitcoin
from .bitcoin import is_address, hash_160, COIN, TYPE_ADDRESS
from .i18n import _
-from .transaction import Transaction, multisig_script
+from .transaction import Transaction, multisig_script, TxOutput
from .paymentrequest import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED
from .plugin import run_hook
t@@ -226,7 +226,7 @@ class Commands:
txin['signatures'] = [None]
txin['num_sig'] = 1
- outputs = [(TYPE_ADDRESS, x['address'], int(x['value'])) for x in outputs]
+ outputs = [TxOutput(TYPE_ADDRESS, x['address'], int(x['value'])) for x in outputs]
tx = Transaction.from_io(inputs, outputs, locktime=locktime)
tx.sign(keypairs)
return tx.as_dict()
t@@ -415,7 +415,7 @@ class Commands:
for address, amount in outputs:
address = self._resolver(address)
amount = satoshis(amount)
- final_outputs.append((TYPE_ADDRESS, address, amount))
+ final_outputs.append(TxOutput(TYPE_ADDRESS, address, amount))
coins = self.wallet.get_spendable_coins(domain, self.config)
tx = self.wallet.make_unsigned_transaction(coins, final_outputs, self.config, fee, change_addr)
DIR diff --git a/electrum/gui/kivy/main_window.py b/electrum/gui/kivy/main_window.py
t@@ -713,13 +713,14 @@ class ElectrumWindow(App):
self.fiat_balance = self.fx.format_amount(c+u+x) + ' [size=22dp]%s[/size]'% self.fx.ccy
def get_max_amount(self):
+ from electrum.transaction import TxOutput
if run_hook('abort_send', self):
return ''
inputs = self.wallet.get_spendable_coins(None, self.electrum_config)
if not inputs:
return ''
addr = str(self.send_screen.screen.address) or self.wallet.dummy_address()
- outputs = [(TYPE_ADDRESS, addr, '!')]
+ outputs = [TxOutput(TYPE_ADDRESS, addr, '!')]
try:
tx = self.wallet.make_unsigned_transaction(inputs, outputs, self.electrum_config)
except NoDynamicFeeEstimates as e:
DIR diff --git a/electrum/gui/kivy/uix/dialogs/__init__.py b/electrum/gui/kivy/uix/dialogs/__init__.py
t@@ -206,9 +206,9 @@ class OutputList(RecycleView):
def update(self, outputs):
res = []
- for (type, address, amount) in outputs:
- value = self.app.format_amount_and_units(amount)
- res.append({'address': address, 'value': value})
+ for o in outputs:
+ value = self.app.format_amount_and_units(o.value)
+ res.append({'address': o.address, 'value': value})
self.data = res
DIR diff --git a/electrum/gui/kivy/uix/screens.py b/electrum/gui/kivy/uix/screens.py
t@@ -20,6 +20,7 @@ from kivy.utils import platform
from electrum.util import profiler, parse_URI, format_time, InvalidPassword, NotEnoughFunds, Fiat
from electrum import bitcoin
+from electrum.transaction import TxOutput
from electrum.util import timestamp_to_datetime
from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED
from electrum.plugin import run_hook
t@@ -256,7 +257,7 @@ class SendScreen(CScreen):
except:
self.app.show_error(_('Invalid amount') + ':\n' + self.screen.amount)
return
- outputs = [(bitcoin.TYPE_ADDRESS, address, amount)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, address, amount)]
message = self.screen.message
amount = sum(map(lambda x:x[2], outputs))
if self.app.electrum_config.get('use_rbf'):
DIR diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py
t@@ -50,7 +50,7 @@ from electrum.util import (format_time, format_satoshis, format_fee_satoshis,
export_meta, import_meta, bh2u, bfh, InvalidPassword,
base_units, base_units_list, base_unit_name_to_decimal_point,
decimal_point_to_base_unit_name, quantize_feerate)
-from electrum.transaction import Transaction
+from electrum.transaction import Transaction, TxOutput
from electrum.address_synchronizer import AddTransactionException
from electrum.wallet import Multisig_Wallet, CannotBumpFee
t@@ -1306,7 +1306,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
outputs = self.payto_e.get_outputs(self.is_max)
if not outputs:
_type, addr = self.get_payto_or_dummy()
- outputs = [(_type, addr, amount)]
+ outputs = [TxOutput(_type, addr, amount)]
is_sweep = bool(self.tx_external_keypairs)
make_tx = lambda fee_est: \
self.wallet.make_unsigned_transaction(
t@@ -1485,14 +1485,14 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
self.show_error(_('No outputs'))
return
- for _type, addr, amount in outputs:
- if addr is None:
+ for o in outputs:
+ if o.address is None:
self.show_error(_('Bitcoin Address is None'))
return
- if _type == TYPE_ADDRESS and not bitcoin.is_address(addr):
+ if o.type == TYPE_ADDRESS and not bitcoin.is_address(o.address):
self.show_error(_('Invalid Bitcoin Address'))
return
- if amount is None:
+ if o.value is None:
self.show_error(_('Invalid Amount'))
return
DIR diff --git a/electrum/gui/qt/paytoedit.py b/electrum/gui/qt/paytoedit.py
t@@ -29,6 +29,7 @@ from decimal import Decimal
from electrum import bitcoin
from electrum.util import bfh
+from electrum.transaction import TxOutput
from .qrtextedit import ScanQRTextEdit
from .completion_text_edit import CompletionTextEdit
t@@ -77,7 +78,7 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit):
x, y = line.split(',')
out_type, out = self.parse_output(x)
amount = self.parse_amount(y)
- return out_type, out, amount
+ return TxOutput(out_type, out, amount)
def parse_output(self, x):
try:
t@@ -139,16 +140,16 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit):
is_max = False
for i, line in enumerate(lines):
try:
- _type, to_address, amount = self.parse_address_and_amount(line)
+ output = self.parse_address_and_amount(line)
except:
self.errors.append((i, line.strip()))
continue
- outputs.append((_type, to_address, amount))
- if amount == '!':
+ outputs.append(output)
+ if output.value == '!':
is_max = True
else:
- total += amount
+ total += output.value
self.win.is_max = is_max
self.outputs = outputs
t@@ -174,7 +175,7 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit):
amount = self.amount_edit.get_amount()
_type, addr = self.payto_address
- self.outputs = [(_type, addr, amount)]
+ self.outputs = [TxOutput(_type, addr, amount)]
return self.outputs[:]
DIR diff --git a/electrum/gui/stdio.py b/electrum/gui/stdio.py
t@@ -4,6 +4,7 @@ _ = lambda x:x
from electrum import WalletStorage, Wallet
from electrum.util import format_satoshis, set_verbosity
from electrum.bitcoin import is_address, COIN, TYPE_ADDRESS
+from electrum.transaction import TxOutput
import getpass, datetime
# minimal fdisk like gui for console usage
t@@ -189,7 +190,8 @@ class ElectrumGui:
if c == "n": return
try:
- tx = self.wallet.mktx([(TYPE_ADDRESS, self.str_recipient, amount)], password, self.config, fee)
+ tx = self.wallet.mktx([TxOutput(TYPE_ADDRESS, self.str_recipient, amount)],
+ password, self.config, fee)
except Exception as e:
print(str(e))
return
DIR diff --git a/electrum/gui/text.py b/electrum/gui/text.py
t@@ -6,6 +6,7 @@ import getpass
import electrum
from electrum.util import format_satoshis, set_verbosity
from electrum.bitcoin import is_address, COIN, TYPE_ADDRESS
+from electrum.transaction import TxOutput
from .. import Wallet, WalletStorage
_ = lambda x:x
t@@ -340,7 +341,8 @@ class ElectrumGui:
else:
password = None
try:
- tx = self.wallet.mktx([(TYPE_ADDRESS, self.str_recipient, amount)], password, self.config, fee)
+ tx = self.wallet.mktx([TxOutput(TYPE_ADDRESS, self.str_recipient, amount)],
+ password, self.config, fee)
except Exception as e:
self.show_message(str(e))
return
DIR diff --git a/electrum/paymentrequest.py b/electrum/paymentrequest.py
t@@ -42,6 +42,7 @@ from .util import print_error, bh2u, bfh
from .util import export_meta, import_meta
from .bitcoin import TYPE_ADDRESS
+from .transaction import TxOutput
REQUEST_HEADERS = {'Accept': 'application/bitcoin-paymentrequest', 'User-Agent': 'Electrum'}
ACK_HEADERS = {'Content-Type':'application/bitcoin-payment','Accept':'application/bitcoin-paymentack','User-Agent':'Electrum'}
t@@ -123,7 +124,7 @@ class PaymentRequest:
self.outputs = []
for o in self.details.outputs:
addr = transaction.get_address_from_output_script(o.script)[1]
- self.outputs.append((TYPE_ADDRESS, addr, o.amount))
+ self.outputs.append(TxOutput(TYPE_ADDRESS, addr, o.amount))
self.memo = self.details.memo
self.payment_url = self.details.payment_url
t@@ -225,8 +226,8 @@ class PaymentRequest:
def get_address(self):
o = self.outputs[0]
- assert o[0] == TYPE_ADDRESS
- return o[1]
+ assert o.type == TYPE_ADDRESS
+ return o.address
def get_requestor(self):
return self.requestor if self.requestor else self.get_address()
DIR diff --git a/electrum/plugins/digitalbitbox/digitalbitbox.py b/electrum/plugins/digitalbitbox/digitalbitbox.py
t@@ -534,9 +534,9 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore):
self.give_error("No matching x_key for sign_transaction") # should never happen
# Build pubkeyarray from outputs
- for _type, address, amount in tx.outputs():
- assert _type == TYPE_ADDRESS
- info = tx.output_info.get(address)
+ for o in tx.outputs():
+ assert o.type == TYPE_ADDRESS
+ info = tx.output_info.get(o.address)
if info is not None:
index, xpubs, m = info
changePath = self.get_derivation() + "/%d/%d" % index
DIR diff --git a/electrum/plugins/hw_wallet/plugin.py b/electrum/plugins/hw_wallet/plugin.py
t@@ -28,7 +28,7 @@ from electrum.plugin import BasePlugin, hook
from electrum.i18n import _
from electrum.bitcoin import is_address, TYPE_SCRIPT
from electrum.util import bfh
-from electrum.transaction import opcodes
+from electrum.transaction import opcodes, TxOutput
class HW_PluginBase(BasePlugin):
t@@ -91,13 +91,13 @@ def is_any_tx_output_on_change_branch(tx):
return False
-def trezor_validate_op_return_output_and_get_data(_type, address, amount):
- if _type != TYPE_SCRIPT:
- raise Exception("Unexpected output type: {}".format(_type))
- script = bfh(address)
+def trezor_validate_op_return_output_and_get_data(output: TxOutput) -> bytes:
+ if output.type != TYPE_SCRIPT:
+ raise Exception("Unexpected output type: {}".format(output.type))
+ script = bfh(output.address)
if not (script[0] == opcodes.OP_RETURN and
script[1] == len(script) - 2 and script[1] <= 75):
raise Exception(_("Only OP_RETURN scripts, with one constant push, are supported."))
- if amount != 0:
+ if output.value != 0:
raise Exception(_("Amount for OP_RETURN output must be zero."))
return script[2:]
DIR diff --git a/electrum/plugins/keepkey/keepkey.py b/electrum/plugins/keepkey/keepkey.py
t@@ -382,7 +382,7 @@ class KeepKeyPlugin(HW_PluginBase):
txoutputtype.amount = amount
if _type == TYPE_SCRIPT:
txoutputtype.script_type = self.types.PAYTOOPRETURN
- txoutputtype.op_return_data = trezor_validate_op_return_output_and_get_data(_type, address, amount)
+ txoutputtype.op_return_data = trezor_validate_op_return_output_and_get_data(o)
elif _type == TYPE_ADDRESS:
if is_segwit_address(address):
txoutputtype.script_type = self.types.PAYTOWITNESS
t@@ -401,7 +401,8 @@ class KeepKeyPlugin(HW_PluginBase):
has_change = False
any_output_on_change_branch = is_any_tx_output_on_change_branch(tx)
- for _type, address, amount in tx.outputs():
+ for o in tx.outputs():
+ _type, address, amount = o.type, o.address, o.value
use_create_by_derivation = False
info = tx.output_info.get(address)
DIR diff --git a/electrum/plugins/ledger/ledger.py b/electrum/plugins/ledger/ledger.py
t@@ -394,9 +394,9 @@ class Ledger_KeyStore(Hardware_KeyStore):
self.give_error("Transaction with more than 2 outputs not supported")
has_change = False
any_output_on_change_branch = is_any_tx_output_on_change_branch(tx)
- for _type, address, amount in tx.outputs():
- assert _type == TYPE_ADDRESS
- info = tx.output_info.get(address)
+ for o in tx.outputs():
+ assert o.type == TYPE_ADDRESS
+ info = tx.output_info.get(o.address)
if (info is not None) and len(tx.outputs()) > 1 \
and not has_change:
index, xpubs, m = info
t@@ -407,9 +407,9 @@ class Ledger_KeyStore(Hardware_KeyStore):
changePath = self.get_derivation()[2:] + "/%d/%d"%index
has_change = True
else:
- output = address
+ output = o.address
else:
- output = address
+ output = o.address
self.handler.show_message(_("Confirm Transaction on your Ledger device..."))
try:
DIR diff --git a/electrum/plugins/safe_t/safe_t.py b/electrum/plugins/safe_t/safe_t.py
t@@ -453,7 +453,7 @@ class SafeTPlugin(HW_PluginBase):
txoutputtype.amount = amount
if _type == TYPE_SCRIPT:
txoutputtype.script_type = self.types.OutputScriptType.PAYTOOPRETURN
- txoutputtype.op_return_data = trezor_validate_op_return_output_and_get_data(_type, address, amount)
+ txoutputtype.op_return_data = trezor_validate_op_return_output_and_get_data(o)
elif _type == TYPE_ADDRESS:
txoutputtype.script_type = self.types.OutputScriptType.PAYTOADDRESS
txoutputtype.address = address
t@@ -463,7 +463,8 @@ class SafeTPlugin(HW_PluginBase):
has_change = False
any_output_on_change_branch = is_any_tx_output_on_change_branch(tx)
- for _type, address, amount in tx.outputs():
+ for o in tx.outputs():
+ _type, address, amount = o.type, o.address, o.value
use_create_by_derivation = False
info = tx.output_info.get(address)
DIR diff --git a/electrum/plugins/trezor/trezor.py b/electrum/plugins/trezor/trezor.py
t@@ -464,7 +464,7 @@ class TrezorPlugin(HW_PluginBase):
txoutputtype.amount = amount
if _type == TYPE_SCRIPT:
txoutputtype.script_type = self.types.OutputScriptType.PAYTOOPRETURN
- txoutputtype.op_return_data = trezor_validate_op_return_output_and_get_data(_type, address, amount)
+ txoutputtype.op_return_data = trezor_validate_op_return_output_and_get_data(o)
elif _type == TYPE_ADDRESS:
txoutputtype.script_type = self.types.OutputScriptType.PAYTOADDRESS
txoutputtype.address = address
t@@ -474,7 +474,8 @@ class TrezorPlugin(HW_PluginBase):
has_change = False
any_output_on_change_branch = is_any_tx_output_on_change_branch(tx)
- for _type, address, amount in tx.outputs():
+ for o in tx.outputs():
+ _type, address, amount = o.type, o.address, o.value
use_create_by_derivation = False
info = tx.output_info.get(address)
DIR diff --git a/electrum/plugins/trustedcoin/trustedcoin.py b/electrum/plugins/trustedcoin/trustedcoin.py
t@@ -33,6 +33,7 @@ from urllib.parse import quote
from electrum import bitcoin, ecc, constants, keystore, version
from electrum.bitcoin import *
+from electrum.transaction import TxOutput
from electrum.mnemonic import Mnemonic
from electrum.wallet import Multisig_Wallet, Deterministic_Wallet
from electrum.i18n import _
t@@ -273,7 +274,7 @@ class Wallet_2fa(Multisig_Wallet):
fee = self.extra_fee(config) if not is_sweep else 0
if fee:
address = self.billing_info['billing_address']
- fee_output = (TYPE_ADDRESS, address, fee)
+ fee_output = TxOutput(TYPE_ADDRESS, address, fee)
try:
tx = mk_tx(outputs + [fee_output])
except NotEnoughFunds:
t@@ -395,9 +396,9 @@ class TrustedCoinPlugin(BasePlugin):
def get_tx_extra_fee(self, wallet, tx):
if type(wallet) != Wallet_2fa:
return
- for _type, addr, amount in tx.outputs():
- if _type == TYPE_ADDRESS and wallet.is_billing_address(addr):
- return addr, amount
+ for o in tx.outputs():
+ if o.type == TYPE_ADDRESS and wallet.is_billing_address(o.address):
+ return o.address, o.value
def finish_requesting(func):
def f(self, *args, **kwargs):
DIR diff --git a/electrum/tests/test_wallet_vertical.py b/electrum/tests/test_wallet_vertical.py
t@@ -10,6 +10,7 @@ from electrum import SimpleConfig
from electrum.address_synchronizer import TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_UNCONF_PARENT
from electrum.wallet import sweep, Multisig_Wallet, Standard_Wallet, Imported_Wallet
from electrum.util import bfh, bh2u
+from electrum.transaction import TxOutput
from electrum.plugins.trustedcoin import trustedcoin
t@@ -532,7 +533,7 @@ class TestWalletSending(TestCaseForTestnet):
wallet1.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
# wallet1 -> wallet2
- outputs = [(bitcoin.TYPE_ADDRESS, wallet2.get_receiving_address(), 250000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, wallet2.get_receiving_address(), 250000)]
tx = wallet1.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
self.assertTrue(tx.is_complete())
t@@ -552,7 +553,7 @@ class TestWalletSending(TestCaseForTestnet):
wallet2.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
# wallet2 -> wallet1
- outputs = [(bitcoin.TYPE_ADDRESS, wallet1.get_receiving_address(), 100000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, wallet1.get_receiving_address(), 100000)]
tx = wallet2.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
self.assertTrue(tx.is_complete())
t@@ -605,7 +606,7 @@ class TestWalletSending(TestCaseForTestnet):
wallet1a.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
# wallet1 -> wallet2
- outputs = [(bitcoin.TYPE_ADDRESS, wallet2.get_receiving_address(), 370000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, wallet2.get_receiving_address(), 370000)]
tx = wallet1a.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
tx = Transaction(tx.serialize()) # simulates moving partial txn between cosigners
self.assertFalse(tx.is_complete())
t@@ -628,7 +629,7 @@ class TestWalletSending(TestCaseForTestnet):
wallet2.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
# wallet2 -> wallet1
- outputs = [(bitcoin.TYPE_ADDRESS, wallet1a.get_receiving_address(), 100000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, wallet1a.get_receiving_address(), 100000)]
tx = wallet2.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
self.assertTrue(tx.is_complete())
t@@ -696,7 +697,7 @@ class TestWalletSending(TestCaseForTestnet):
wallet1a.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
# wallet1 -> wallet2
- outputs = [(bitcoin.TYPE_ADDRESS, wallet2a.get_receiving_address(), 165000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, wallet2a.get_receiving_address(), 165000)]
tx = wallet1a.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
txid = tx.txid()
tx = Transaction(tx.serialize()) # simulates moving partial txn between cosigners
t@@ -722,7 +723,7 @@ class TestWalletSending(TestCaseForTestnet):
wallet2a.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
# wallet2 -> wallet1
- outputs = [(bitcoin.TYPE_ADDRESS, wallet1a.get_receiving_address(), 100000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, wallet1a.get_receiving_address(), 100000)]
tx = wallet2a.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
txid = tx.txid()
tx = Transaction(tx.serialize()) # simulates moving partial txn between cosigners
t@@ -776,7 +777,7 @@ class TestWalletSending(TestCaseForTestnet):
wallet1a.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
# wallet1 -> wallet2
- outputs = [(bitcoin.TYPE_ADDRESS, wallet2.get_receiving_address(), 1000000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, wallet2.get_receiving_address(), 1000000)]
tx = wallet1a.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
self.assertTrue(tx.is_complete())
t@@ -796,7 +797,7 @@ class TestWalletSending(TestCaseForTestnet):
wallet2.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED)
# wallet2 -> wallet1
- outputs = [(bitcoin.TYPE_ADDRESS, wallet1a.get_receiving_address(), 300000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, wallet1a.get_receiving_address(), 300000)]
tx = wallet2.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
self.assertTrue(tx.is_complete())
t@@ -832,7 +833,7 @@ class TestWalletSending(TestCaseForTestnet):
wallet.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
# create tx
- outputs = [(bitcoin.TYPE_ADDRESS, '2N1VTMMFb91SH9SNRAkT7z8otP5eZEct4KL', 2500000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, '2N1VTMMFb91SH9SNRAkT7z8otP5eZEct4KL', 2500000)]
coins = wallet.get_spendable_coins(domain=None, config=self.config)
tx = wallet.make_unsigned_transaction(coins, outputs, config=self.config, fixed_fee=5000)
tx.set_rbf(True)
t@@ -918,7 +919,7 @@ class TestWalletSending(TestCaseForTestnet):
wallet.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
# create tx
- outputs = [(bitcoin.TYPE_ADDRESS, '2N1VTMMFb91SH9SNRAkT7z8otP5eZEct4KL', 2500000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, '2N1VTMMFb91SH9SNRAkT7z8otP5eZEct4KL', 2500000)]
coins = wallet.get_spendable_coins(domain=None, config=self.config)
tx = wallet.make_unsigned_transaction(coins, outputs, config=self.config, fixed_fee=5000)
tx.set_rbf(True)
t@@ -1048,7 +1049,7 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
wallet_online.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
# create unsigned tx
- outputs = [(bitcoin.TYPE_ADDRESS, 'tb1qp0mv2sxsyxxfj5gl0332f9uyez93su9cf26757', 2500000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, 'tb1qp0mv2sxsyxxfj5gl0332f9uyez93su9cf26757', 2500000)]
tx = wallet_online.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
tx.set_rbf(True)
tx.locktime = 1325340
t@@ -1088,7 +1089,7 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
wallet_online.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
# create unsigned tx
- outputs = [(bitcoin.TYPE_ADDRESS, 'tb1qp0mv2sxsyxxfj5gl0332f9uyez93su9cf26757', 2500000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, 'tb1qp0mv2sxsyxxfj5gl0332f9uyez93su9cf26757', 2500000)]
tx = wallet_online.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
tx.set_rbf(True)
tx.locktime = 1325341
t@@ -1129,7 +1130,7 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
wallet_online.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
# create unsigned tx
- outputs = [(bitcoin.TYPE_ADDRESS, 'tb1qp0mv2sxsyxxfj5gl0332f9uyez93su9cf26757', 2500000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, 'tb1qp0mv2sxsyxxfj5gl0332f9uyez93su9cf26757', 2500000)]
tx = wallet_online.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
tx.set_rbf(True)
tx.locktime = 1325341
t@@ -1165,7 +1166,7 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
wallet_online.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
# create unsigned tx
- outputs = [(bitcoin.TYPE_ADDRESS, 'tb1quk7ahlhr3qmjndy0uvu9y9hxfesrtahtta9ghm', 2500000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, 'tb1quk7ahlhr3qmjndy0uvu9y9hxfesrtahtta9ghm', 2500000)]
tx = wallet_online.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
tx.set_rbf(True)
tx.locktime = 1325340
t@@ -1199,7 +1200,7 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
wallet_online.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
# create unsigned tx
- outputs = [(bitcoin.TYPE_ADDRESS, 'tb1quk7ahlhr3qmjndy0uvu9y9hxfesrtahtta9ghm', 2500000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, 'tb1quk7ahlhr3qmjndy0uvu9y9hxfesrtahtta9ghm', 2500000)]
tx = wallet_online.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
tx.set_rbf(True)
tx.locktime = 1325340
t@@ -1233,7 +1234,7 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
wallet_online.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
# create unsigned tx
- outputs = [(bitcoin.TYPE_ADDRESS, 'tb1quk7ahlhr3qmjndy0uvu9y9hxfesrtahtta9ghm', 2500000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, 'tb1quk7ahlhr3qmjndy0uvu9y9hxfesrtahtta9ghm', 2500000)]
tx = wallet_online.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
tx.set_rbf(True)
tx.locktime = 1325340
t@@ -1270,7 +1271,7 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
wallet_online.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
# create unsigned tx
- outputs = [(bitcoin.TYPE_ADDRESS, 'tb1quk7ahlhr3qmjndy0uvu9y9hxfesrtahtta9ghm', 2500000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, 'tb1quk7ahlhr3qmjndy0uvu9y9hxfesrtahtta9ghm', 2500000)]
tx = wallet_online.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
tx.set_rbf(True)
tx.locktime = 1325340
t@@ -1307,7 +1308,7 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
wallet_online.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
# create unsigned tx
- outputs = [(bitcoin.TYPE_ADDRESS, 'tb1quk7ahlhr3qmjndy0uvu9y9hxfesrtahtta9ghm', 2500000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, 'tb1quk7ahlhr3qmjndy0uvu9y9hxfesrtahtta9ghm', 2500000)]
tx = wallet_online.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
tx.set_rbf(True)
tx.locktime = 1325340
t@@ -1344,7 +1345,7 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
wallet_online.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
# create unsigned tx
- outputs = [(bitcoin.TYPE_ADDRESS, 'tb1quk7ahlhr3qmjndy0uvu9y9hxfesrtahtta9ghm', 2500000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, 'tb1quk7ahlhr3qmjndy0uvu9y9hxfesrtahtta9ghm', 2500000)]
tx = wallet_online.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
tx.set_rbf(True)
tx.locktime = 1325340
t@@ -1393,7 +1394,7 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
wallet_online.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
# create unsigned tx
- outputs = [(bitcoin.TYPE_ADDRESS, '2MuCQQHJNnrXzQzuqfUCfAwAjPqpyEHbgue', 2500000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, '2MuCQQHJNnrXzQzuqfUCfAwAjPqpyEHbgue', 2500000)]
tx = wallet_online.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
tx.set_rbf(True)
tx.locktime = 1325503
t@@ -1450,7 +1451,7 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
wallet_online.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
# create unsigned tx
- outputs = [(bitcoin.TYPE_ADDRESS, '2N8CtJRwxb2GCaiWWdSHLZHHLoZy53CCyxf', 2500000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, '2N8CtJRwxb2GCaiWWdSHLZHHLoZy53CCyxf', 2500000)]
tx = wallet_online.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
tx.set_rbf(True)
tx.locktime = 1325504
t@@ -1509,7 +1510,7 @@ class TestWalletOfflineSigning(TestCaseForTestnet):
wallet_online.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED)
# create unsigned tx
- outputs = [(bitcoin.TYPE_ADDRESS, '2MyoZVy8T1t94yLmyKu8DP1SmbWvnxbkwRA', 2500000)]
+ outputs = [TxOutput(bitcoin.TYPE_ADDRESS, '2MyoZVy8T1t94yLmyKu8DP1SmbWvnxbkwRA', 2500000)]
tx = wallet_online.mktx(outputs=outputs, password=None, config=self.config, fee=5000)
tx.set_rbf(True)
tx.locktime = 1325505
DIR diff --git a/electrum/transaction.py b/electrum/transaction.py
t@@ -27,7 +27,7 @@
# Note: The deserialization code originally comes from ABE.
-from typing import Sequence, Union
+from typing import Sequence, Union, NamedTuple
from .util import print_error, profiler
t@@ -59,6 +59,10 @@ class NotRecognizedRedeemScript(Exception):
pass
+TxOutput = NamedTuple("TxOutput", [('type', int), ('address', str), ('value', Union[int, str])])
+# ^ value is str when the output is set to max: '!'
+
+
class BCDataStream(object):
def __init__(self):
self.input = None
t@@ -721,7 +725,7 @@ class Transaction:
return
d = deserialize(self.raw, force_full_parse)
self._inputs = d['inputs']
- self._outputs = [(x['type'], x['address'], x['value']) for x in d['outputs']]
+ self._outputs = [TxOutput(x['type'], x['address'], x['value']) for x in d['outputs']]
self.locktime = d['lockTime']
self.version = d['version']
self.is_partial_originally = d['partial']
t@@ -1180,17 +1184,17 @@ class Transaction:
def get_outputs(self):
"""convert pubkeys to addresses"""
- o = []
- for type, x, v in self.outputs():
- if type == TYPE_ADDRESS:
- addr = x
- elif type == TYPE_PUBKEY:
+ outputs = []
+ for o in self.outputs():
+ if o.type == TYPE_ADDRESS:
+ addr = o.address
+ elif o.type == TYPE_PUBKEY:
# TODO do we really want this conversion? it's not really that address after all
- addr = bitcoin.public_key_to_p2pkh(bfh(x))
+ addr = bitcoin.public_key_to_p2pkh(bfh(o.address))
else:
- addr = 'SCRIPT ' + x
- o.append((addr,v)) # consider using yield (addr, v)
- return o
+ addr = 'SCRIPT ' + o.address
+ outputs.append((addr, o.value)) # consider using yield (addr, v)
+ return outputs
def get_output_addresses(self):
return [addr for addr, val in self.get_outputs()]
DIR diff --git a/electrum/wallet.py b/electrum/wallet.py
t@@ -51,7 +51,7 @@ from .keystore import load_keystore, Hardware_KeyStore
from .storage import multisig_type, STO_EV_PLAINTEXT, STO_EV_USER_PW, STO_EV_XPUB_PW
from . import transaction, bitcoin, coinchooser, paymentrequest, contacts
-from .transaction import Transaction
+from .transaction import Transaction, TxOutput
from .plugin import run_hook
from .address_synchronizer import (AddressSynchronizer, TX_HEIGHT_LOCAL,
TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED)
t@@ -133,7 +133,7 @@ def sweep(privkeys, network, config, recipient, fee=None, imax=100):
inputs, keypairs = sweep_preparations(privkeys, network, imax)
total = sum(i.get('value') for i in inputs)
if fee is None:
- outputs = [(TYPE_ADDRESS, recipient, total)]
+ outputs = [TxOutput(TYPE_ADDRESS, recipient, total)]
tx = Transaction.from_io(inputs, outputs)
fee = config.estimate_fee(tx.estimated_size())
if total - fee < 0:
t@@ -141,7 +141,7 @@ def sweep(privkeys, network, config, recipient, fee=None, imax=100):
if total - fee < dust_threshold(network):
raise Exception(_('Not enough funds on address.') + '\nTotal: %d satoshis\nFee: %d\nDust Threshold: %d'%(total, fee, dust_threshold(network)))
- outputs = [(TYPE_ADDRESS, recipient, total - fee)]
+ outputs = [TxOutput(TYPE_ADDRESS, recipient, total - fee)]
locktime = network.get_local_height()
tx = Transaction.from_io(inputs, outputs, locktime=locktime)
t@@ -538,11 +538,10 @@ class Abstract_Wallet(AddressSynchronizer):
# check outputs
i_max = None
for i, o in enumerate(outputs):
- _type, data, value = o
- if _type == TYPE_ADDRESS:
- if not is_address(data):
- raise Exception("Invalid bitcoin address: {}".format(data))
- if value == '!':
+ if o.type == TYPE_ADDRESS:
+ if not is_address(o.address):
+ raise Exception("Invalid bitcoin address: {}".format(o.address))
+ if o.value == '!':
if i_max is not None:
raise Exception("More than one output set to spend max")
i_max = i
t@@ -593,14 +592,13 @@ class Abstract_Wallet(AddressSynchronizer):
else:
# FIXME?? this might spend inputs with negative effective value...
sendable = sum(map(lambda x:x['value'], inputs))
- _type, data, value = outputs[i_max]
- outputs[i_max] = (_type, data, 0)
+ outputs[i_max] = outputs[i_max]._replace(value=0)
tx = Transaction.from_io(inputs, outputs[:])
fee = fee_estimator(tx.estimated_size())
amount = sendable - tx.output_value() - fee
if amount < 0:
raise NotEnoughFunds()
- outputs[i_max] = (_type, data, amount)
+ outputs[i_max] = outputs[i_max]._replace(value=amount)
tx = Transaction.from_io(inputs, outputs[:])
# Sort the inputs and outputs deterministically
t@@ -694,14 +692,13 @@ class Abstract_Wallet(AddressSynchronizer):
s = sorted(s, key=lambda x: x[2])
for o in s:
i = outputs.index(o)
- otype, address, value = o
- if value - delta >= self.dust_threshold():
- outputs[i] = otype, address, value - delta
+ if o.value - delta >= self.dust_threshold():
+ outputs[i] = o._replace(value=o.value-delta)
delta = 0
break
else:
del outputs[i]
- delta -= value
+ delta -= o.value
if delta > 0:
continue
if delta > 0:
t@@ -714,8 +711,8 @@ class Abstract_Wallet(AddressSynchronizer):
def cpfp(self, tx, fee):
txid = tx.txid()
for i, o in enumerate(tx.outputs()):
- otype, address, value = o
- if otype == TYPE_ADDRESS and self.is_mine(address):
+ address, value = o.address, o.value
+ if o.type == TYPE_ADDRESS and self.is_mine(address):
break
else:
return
t@@ -725,7 +722,7 @@ class Abstract_Wallet(AddressSynchronizer):
return
self.add_input_info(item)
inputs = [item]
- outputs = [(TYPE_ADDRESS, address, value - fee)]
+ outputs = [TxOutput(TYPE_ADDRESS, address, value - fee)]
locktime = self.get_local_height()
# note: no need to call tx.BIP_LI01_sort() here - single input/output
return Transaction.from_io(inputs, outputs, locktime=locktime)