replace tx.input, tx.output by methods, so that deserialize calls are encapsulated
This commit is contained in:
parent
321ab10742
commit
d200b236ae
|
@ -235,7 +235,7 @@ class TxDialog(QDialog, MessageBoxMixin):
|
|||
if self.tx.locktime > 0:
|
||||
vbox.addWidget(QLabel("LockTime: %d\n" % self.tx.locktime))
|
||||
|
||||
vbox.addWidget(QLabel(_("Inputs") + ' (%d)'%len(self.tx.inputs)))
|
||||
vbox.addWidget(QLabel(_("Inputs") + ' (%d)'%len(self.tx.inputs())))
|
||||
|
||||
ext = QTextCharFormat()
|
||||
rec = QTextCharFormat()
|
||||
|
@ -258,7 +258,7 @@ class TxDialog(QDialog, MessageBoxMixin):
|
|||
i_text.setReadOnly(True)
|
||||
i_text.setMaximumHeight(100)
|
||||
cursor = i_text.textCursor()
|
||||
for x in self.tx.inputs:
|
||||
for x in self.tx.inputs():
|
||||
if x.get('is_coinbase'):
|
||||
cursor.insertText('coinbase')
|
||||
else:
|
||||
|
@ -279,7 +279,7 @@ class TxDialog(QDialog, MessageBoxMixin):
|
|||
cursor.insertBlock()
|
||||
|
||||
vbox.addWidget(i_text)
|
||||
vbox.addWidget(QLabel(_("Outputs") + ' (%d)'%len(self.tx.outputs)))
|
||||
vbox.addWidget(QLabel(_("Outputs") + ' (%d)'%len(self.tx.outputs())))
|
||||
o_text = QTextEdit()
|
||||
o_text.setFont(QFont(MONOSPACE_FONT))
|
||||
o_text.setReadOnly(True)
|
||||
|
|
|
@ -99,7 +99,7 @@ class CoinChooserBase(PrintError):
|
|||
|
||||
def change_amounts(self, tx, count, fee_estimator, dust_threshold):
|
||||
# Break change up if bigger than max_change
|
||||
output_amounts = [o[2] for o in tx.outputs]
|
||||
output_amounts = [o[2] for o in tx.outputs()]
|
||||
max_change = max(max(output_amounts) * 1.25, dust_threshold * 10)
|
||||
|
||||
# Use N change outputs
|
||||
|
@ -187,16 +187,16 @@ class CoinChooserBase(PrintError):
|
|||
buckets = self.choose_buckets(buckets, sufficient_funds,
|
||||
self.penalty_func(tx))
|
||||
|
||||
tx.inputs = [coin for b in buckets for coin in b.coins]
|
||||
tx.add_inputs([coin for b in buckets for coin in b.coins])
|
||||
tx_size = base_size + sum(bucket.size for bucket in buckets)
|
||||
|
||||
# This takes a count of change outputs and returns a tx fee;
|
||||
# each pay-to-bitcoin-address output serializes as 34 bytes
|
||||
fee = lambda count: fee_estimator(tx_size + count * 34)
|
||||
change = self.change_outputs(tx, change_addrs, fee, dust_threshold)
|
||||
tx.outputs.extend(change)
|
||||
tx.add_outputs(change)
|
||||
|
||||
self.print_error("using %d inputs" % len(tx.inputs))
|
||||
self.print_error("using %d inputs" % len(tx.inputs()))
|
||||
self.print_error("using buckets:", [bucket.desc for bucket in buckets])
|
||||
|
||||
return tx
|
||||
|
@ -282,9 +282,9 @@ class CoinChooserPrivacy(CoinChooserRandom):
|
|||
raise NotImplementedError
|
||||
|
||||
def penalty_func(self, tx):
|
||||
min_change = min(o[2] for o in tx.outputs) * 0.75
|
||||
max_change = max(o[2] for o in tx.outputs) * 1.33
|
||||
spent_amount = sum(o[2] for o in tx.outputs)
|
||||
min_change = min(o[2] for o in tx.outputs()) * 0.75
|
||||
max_change = max(o[2] for o in tx.outputs()) * 1.33
|
||||
spent_amount = sum(o[2] for o in tx.outputs())
|
||||
|
||||
def penalty(buckets):
|
||||
badness = len(buckets) - 1
|
||||
|
|
|
@ -210,7 +210,6 @@ class Commands:
|
|||
def signtransaction(self, tx, privkey=None):
|
||||
"""Sign a transaction. The wallet keys will be used unless a private key is provided."""
|
||||
t = Transaction(tx)
|
||||
t.deserialize()
|
||||
if privkey:
|
||||
pubkey = bitcoin.public_key_from_private_key(privkey)
|
||||
t.sign({pubkey:privkey})
|
||||
|
@ -221,8 +220,7 @@ class Commands:
|
|||
@command('')
|
||||
def deserialize(self, tx):
|
||||
"""Deserialize a serialized transaction"""
|
||||
t = Transaction(tx)
|
||||
return t.deserialize()
|
||||
return Transaction(tx).deserialize()
|
||||
|
||||
@command('n')
|
||||
def broadcast(self, tx):
|
||||
|
|
|
@ -479,17 +479,27 @@ class Transaction:
|
|||
self.raw = raw['hex']
|
||||
else:
|
||||
raise BaseException("cannot initialize transaction", raw)
|
||||
self.inputs = None
|
||||
self._inputs = None
|
||||
|
||||
def update(self, raw):
|
||||
self.raw = raw
|
||||
self.inputs = None
|
||||
self._inputs = None
|
||||
self.deserialize()
|
||||
|
||||
def inputs(self):
|
||||
if self._inputs is None:
|
||||
self.deserialize()
|
||||
return self._inputs
|
||||
|
||||
def outputs(self):
|
||||
if self._outputs is None:
|
||||
self.deserialize()
|
||||
return self._outputs
|
||||
|
||||
def update_signatures(self, raw):
|
||||
"""Add new signatures to a transaction"""
|
||||
d = deserialize(raw)
|
||||
for i, txin in enumerate(self.inputs):
|
||||
for i, txin in enumerate(self.inputs()):
|
||||
sigs1 = txin.get('signatures')
|
||||
sigs2 = d['inputs'][i].get('signatures')
|
||||
for sig in sigs2:
|
||||
|
@ -509,8 +519,8 @@ class Transaction:
|
|||
public_key.verify_digest(sig_string, for_sig, sigdecode = ecdsa.util.sigdecode_string)
|
||||
j = pubkeys.index(pubkey)
|
||||
print_error("adding sig", i, j, pubkey, sig)
|
||||
self.inputs[i]['signatures'][j] = sig
|
||||
self.inputs[i]['x_pubkeys'][j] = pubkey
|
||||
self._inputs[i]['signatures'][j] = sig
|
||||
self._inputs[i]['x_pubkeys'][j] = pubkey
|
||||
break
|
||||
# redo raw
|
||||
self.raw = self.serialize()
|
||||
|
@ -519,19 +529,19 @@ class Transaction:
|
|||
def deserialize(self):
|
||||
if self.raw is None:
|
||||
self.raw = self.serialize()
|
||||
if self.inputs is not None:
|
||||
if self._inputs is not None:
|
||||
return
|
||||
d = deserialize(self.raw)
|
||||
self.inputs = d['inputs']
|
||||
self.outputs = [(x['type'], x['address'], x['value']) for x in d['outputs']]
|
||||
self._inputs = d['inputs']
|
||||
self._outputs = [(x['type'], x['address'], x['value']) for x in d['outputs']]
|
||||
self.locktime = d['lockTime']
|
||||
return d
|
||||
|
||||
@classmethod
|
||||
def from_io(klass, inputs, outputs, locktime=0):
|
||||
self = klass(None)
|
||||
self.inputs = inputs
|
||||
self.outputs = outputs
|
||||
self._inputs = inputs
|
||||
self._outputs = outputs
|
||||
self.locktime = locktime
|
||||
return self
|
||||
|
||||
|
@ -657,12 +667,12 @@ class Transaction:
|
|||
|
||||
def BIP_LI01_sort(self):
|
||||
# See https://github.com/kristovatlas/rfc/blob/master/bips/bip-li01.mediawiki
|
||||
self.inputs.sort(key = lambda i: (i['prevout_hash'], i['prevout_n']))
|
||||
self.outputs.sort(key = lambda o: (o[2], self.pay_script(o[0], o[1])))
|
||||
self._inputs.sort(key = lambda i: (i['prevout_hash'], i['prevout_n']))
|
||||
self._outputs.sort(key = lambda o: (o[2], self.pay_script(o[0], o[1])))
|
||||
|
||||
def serialize(self, for_sig=None):
|
||||
inputs = self.inputs
|
||||
outputs = self.outputs
|
||||
inputs = self.inputs()
|
||||
outputs = self.outputs()
|
||||
s = int_to_hex(1,4) # version
|
||||
s += var_int( len(inputs) ) # number of inputs
|
||||
for i, txin in enumerate(inputs):
|
||||
|
@ -685,21 +695,25 @@ class Transaction:
|
|||
def hash(self):
|
||||
return Hash(self.raw.decode('hex') )[::-1].encode('hex')
|
||||
|
||||
def add_input(self, input):
|
||||
self.inputs.append(input)
|
||||
def add_inputs(self, inputs):
|
||||
self._inputs.extend(inputs)
|
||||
self.raw = None
|
||||
|
||||
def add_outputs(self, outputs):
|
||||
self._outputs.extend(outputs)
|
||||
self.raw = None
|
||||
|
||||
def input_value(self):
|
||||
return sum(x['value'] for x in self.inputs)
|
||||
return sum(x['value'] for x in self.inputs())
|
||||
|
||||
def output_value(self):
|
||||
return sum( val for tp,addr,val in self.outputs)
|
||||
return sum( val for tp,addr,val in self.outputs())
|
||||
|
||||
def get_fee(self):
|
||||
return self.input_value() - self.output_value()
|
||||
|
||||
def is_final(self):
|
||||
return not any([x.get('sequence') < 0xffffffff - 1 for x in self.inputs])
|
||||
return not any([x.get('sequence') < 0xffffffff - 1 for x in self.inputs()])
|
||||
|
||||
@classmethod
|
||||
def fee_for_size(self, relay_fee, fee_per_kb, size):
|
||||
|
@ -727,7 +741,7 @@ class Transaction:
|
|||
def signature_count(self):
|
||||
r = 0
|
||||
s = 0
|
||||
for txin in self.inputs:
|
||||
for txin in self.inputs():
|
||||
if txin.get('is_coinbase'):
|
||||
continue
|
||||
signatures = filter(None, txin.get('signatures',[]))
|
||||
|
@ -741,14 +755,14 @@ class Transaction:
|
|||
|
||||
def inputs_without_script(self):
|
||||
out = set()
|
||||
for i, txin in enumerate(self.inputs):
|
||||
for i, txin in enumerate(self.inputs()):
|
||||
if txin.get('scriptSig') == '':
|
||||
out.add(i)
|
||||
return out
|
||||
|
||||
def inputs_to_sign(self):
|
||||
out = set()
|
||||
for txin in self.inputs:
|
||||
for txin in self.inputs():
|
||||
num_sig = txin.get('num_sig')
|
||||
if num_sig is None:
|
||||
continue
|
||||
|
@ -765,7 +779,7 @@ class Transaction:
|
|||
return out
|
||||
|
||||
def sign(self, keypairs):
|
||||
for i, txin in enumerate(self.inputs):
|
||||
for i, txin in enumerate(self.inputs()):
|
||||
num = txin['num_sig']
|
||||
for x_pubkey in txin['x_pubkeys']:
|
||||
signatures = filter(None, txin['signatures'])
|
||||
|
@ -775,14 +789,14 @@ class Transaction:
|
|||
if x_pubkey in keypairs.keys():
|
||||
print_error("adding signature for", x_pubkey)
|
||||
# add pubkey to txin
|
||||
txin = self.inputs[i]
|
||||
txin = self._inputs[i]
|
||||
x_pubkeys = txin['x_pubkeys']
|
||||
ii = x_pubkeys.index(x_pubkey)
|
||||
sec = keypairs[x_pubkey]
|
||||
pubkey = public_key_from_private_key(sec)
|
||||
txin['x_pubkeys'][ii] = pubkey
|
||||
txin['pubkeys'][ii] = pubkey
|
||||
self.inputs[i] = txin
|
||||
self._inputs[i] = txin
|
||||
# add signature
|
||||
for_sig = Hash(self.tx_for_sig(i).decode('hex'))
|
||||
pkey = regenerate_key(sec)
|
||||
|
@ -792,7 +806,7 @@ class Transaction:
|
|||
sig = private_key.sign_digest_deterministic( for_sig, hashfunc=hashlib.sha256, sigencode = ecdsa.util.sigencode_der )
|
||||
assert public_key.verify_digest( sig, for_sig, sigdecode = ecdsa.util.sigdecode_der)
|
||||
txin['signatures'][ii] = sig.encode('hex')
|
||||
self.inputs[i] = txin
|
||||
self._inputs[i] = txin
|
||||
print_error("is_complete", self.is_complete())
|
||||
self.raw = self.serialize()
|
||||
|
||||
|
@ -800,7 +814,7 @@ class Transaction:
|
|||
def get_outputs(self):
|
||||
"""convert pubkeys to addresses"""
|
||||
o = []
|
||||
for type, x, v in self.outputs:
|
||||
for type, x, v in self.outputs():
|
||||
if type == TYPE_ADDRESS:
|
||||
addr = x
|
||||
elif type == TYPE_PUBKEY:
|
||||
|
@ -815,7 +829,7 @@ class Transaction:
|
|||
|
||||
|
||||
def has_address(self, addr):
|
||||
return (addr in self.get_output_addresses()) or (addr in (tx.get("address") for tx in self.inputs))
|
||||
return (addr in self.get_output_addresses()) or (addr in (tx.get("address") for tx in self.inputs()))
|
||||
|
||||
def as_dict(self):
|
||||
if self.raw is None:
|
||||
|
@ -842,7 +856,7 @@ class Transaction:
|
|||
# priority must be large enough for free tx
|
||||
threshold = 57600000
|
||||
weight = 0
|
||||
for txin in self.inputs:
|
||||
for txin in self.inputs():
|
||||
age = wallet.get_confirmations(txin["prevout_hash"])[0]
|
||||
weight += txin["value"] * age
|
||||
priority = weight / size
|
||||
|
|
|
@ -274,7 +274,6 @@ class Abstract_Wallet(PrintError):
|
|||
continue
|
||||
tx = self.transactions.get(tx_hash)
|
||||
if tx is not None:
|
||||
tx.deserialize()
|
||||
self.add_transaction(tx_hash, tx)
|
||||
save = True
|
||||
if save:
|
||||
|
@ -541,7 +540,7 @@ class Abstract_Wallet(PrintError):
|
|||
is_pruned = False
|
||||
is_partial = False
|
||||
v_in = v_out = v_out_mine = 0
|
||||
for item in tx.inputs:
|
||||
for item in tx.inputs():
|
||||
addr = item.get('address')
|
||||
if addr in addresses:
|
||||
is_send = True
|
||||
|
@ -722,11 +721,11 @@ class Abstract_Wallet(PrintError):
|
|||
return addr
|
||||
|
||||
def add_transaction(self, tx_hash, tx):
|
||||
is_coinbase = tx.inputs[0].get('is_coinbase') == True
|
||||
is_coinbase = tx.inputs()[0].get('is_coinbase') == True
|
||||
with self.transaction_lock:
|
||||
# add inputs
|
||||
self.txi[tx_hash] = d = {}
|
||||
for txi in tx.inputs:
|
||||
for txi in tx.inputs():
|
||||
addr = txi.get('address')
|
||||
if not txi.get('is_coinbase'):
|
||||
prevout_hash = txi['prevout_hash']
|
||||
|
@ -748,7 +747,7 @@ class Abstract_Wallet(PrintError):
|
|||
|
||||
# add outputs
|
||||
self.txo[tx_hash] = d = {}
|
||||
for n, txo in enumerate(tx.outputs):
|
||||
for n, txo in enumerate(tx.outputs()):
|
||||
ser = tx_hash + ':%d'%n
|
||||
_type, x, v = txo
|
||||
if _type == TYPE_ADDRESS:
|
||||
|
@ -827,7 +826,6 @@ class Abstract_Wallet(PrintError):
|
|||
# if addr is new, we have to recompute txi and txo
|
||||
tx = self.transactions.get(tx_hash)
|
||||
if tx is not None and self.txi.get(tx_hash, {}).get(addr) is None and self.txo.get(tx_hash, {}).get(addr) is None:
|
||||
tx.deserialize()
|
||||
self.add_transaction(tx_hash, tx)
|
||||
|
||||
# Write updated TXI, TXO etc.
|
||||
|
@ -1010,7 +1008,7 @@ class Abstract_Wallet(PrintError):
|
|||
self.check_password(password)
|
||||
# Add derivation for utxo in wallets
|
||||
for i, addr in self.utxo_can_sign(tx):
|
||||
txin = tx.inputs[i]
|
||||
txin = tx.inputs()[i]
|
||||
txin['address'] = addr
|
||||
self.add_input_info(txin)
|
||||
# Add private keys
|
||||
|
|
|
@ -200,9 +200,9 @@ class BTChipWallet(BIP44_Wallet):
|
|||
pubKeys.append(self.get_public_keys(address))
|
||||
|
||||
# Recognize outputs - only one output and one change is authorized
|
||||
if len(tx.outputs) > 2: # should never happen
|
||||
if len(tx.outputs()) > 2: # should never happen
|
||||
self.give_error("Transaction with more than 2 outputs not supported")
|
||||
for type, address, amount in tx.outputs:
|
||||
for type, address, amount in tx.outputs():
|
||||
assert type == TYPE_ADDRESS
|
||||
if self.is_change(address):
|
||||
changePath = self.address_id(address)
|
||||
|
|
|
@ -365,7 +365,7 @@ class TrezorCompatiblePlugin(BasePlugin, ThreadJob):
|
|||
|
||||
def tx_inputs(self, tx, for_sig=False):
|
||||
inputs = []
|
||||
for txin in tx.inputs:
|
||||
for txin in tx.inputs():
|
||||
txinputtype = self.types.TxInputType()
|
||||
if txin.get('is_coinbase'):
|
||||
prev_hash = "\0"*32
|
||||
|
@ -426,8 +426,7 @@ class TrezorCompatiblePlugin(BasePlugin, ThreadJob):
|
|||
|
||||
def tx_outputs(self, wallet, tx):
|
||||
outputs = []
|
||||
|
||||
for type, address, amount in tx.outputs:
|
||||
for type, address, amount in tx.outputs():
|
||||
assert type == TYPE_ADDRESS
|
||||
txoutputtype = self.types.TxOutputType()
|
||||
if wallet.is_change(address):
|
||||
|
@ -464,7 +463,6 @@ class TrezorCompatiblePlugin(BasePlugin, ThreadJob):
|
|||
# This function is called from the trezor libraries (via tx_api)
|
||||
def get_tx(self, tx_hash):
|
||||
tx = self.prev_tx[tx_hash]
|
||||
tx.deserialize()
|
||||
return self.electrum_tx_to_txtype(tx)
|
||||
|
||||
@staticmethod
|
||||
|
|
Loading…
Reference in New Issue