|
|
@ -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 |
|
|
|