mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-23 17:47:31 +00:00
optimized, cleaned up, commented
This commit is contained in:
parent
7860bcfaf7
commit
5c12c2bc2f
2 changed files with 73 additions and 80 deletions
|
@ -386,15 +386,13 @@ def parse_scriptSig(d, bytes):
|
||||||
return
|
return
|
||||||
|
|
||||||
# p2sh transaction, 2 of n
|
# p2sh transaction, 2 of n
|
||||||
match = [ opcodes.OP_0 ]
|
match = [ opcodes.OP_0 ] + [ opcodes.OP_PUSHDATA4 ] * (len(decoded) - 1)
|
||||||
while len(match) < len(decoded):
|
|
||||||
match.append(opcodes.OP_PUSHDATA4)
|
|
||||||
|
|
||||||
if not match_decoded(decoded, match):
|
if not match_decoded(decoded, match):
|
||||||
print_error("cannot find address in input script", bytes.encode('hex'))
|
print_error("cannot find address in input script", bytes.encode('hex'))
|
||||||
return
|
return
|
||||||
|
|
||||||
x_sig = map(lambda x:x[1].encode('hex'), decoded[1:-1])
|
x_sig = [x[1].encode('hex') for x in decoded[1:-1]]
|
||||||
d['signatures'] = parse_sig(x_sig)
|
d['signatures'] = parse_sig(x_sig)
|
||||||
d['num_sig'] = 2
|
d['num_sig'] = 2
|
||||||
|
|
||||||
|
@ -410,7 +408,7 @@ def parse_scriptSig(d, bytes):
|
||||||
return
|
return
|
||||||
|
|
||||||
d['x_pubkeys'] = x_pubkeys
|
d['x_pubkeys'] = x_pubkeys
|
||||||
pubkeys = map(lambda x: parse_xpub(x)[0], x_pubkeys)
|
pubkeys = [parse_xpub(x)[0] for x in x_pubkeys] # xpub, addr = parse_xpub()
|
||||||
d['pubkeys'] = pubkeys
|
d['pubkeys'] = pubkeys
|
||||||
redeemScript = Transaction.multisig_script(pubkeys,2)
|
redeemScript = Transaction.multisig_script(pubkeys,2)
|
||||||
d['redeemScript'] = redeemScript
|
d['redeemScript'] = redeemScript
|
||||||
|
@ -495,7 +493,9 @@ def deserialize(raw):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
push_script = lambda x: op_push(len(x)/2) + x
|
def push_script(x):
|
||||||
|
return op_push(len(x)/2) + x
|
||||||
|
|
||||||
|
|
||||||
class Transaction:
|
class Transaction:
|
||||||
|
|
||||||
|
@ -520,7 +520,7 @@ class Transaction:
|
||||||
d = deserialize(raw)
|
d = deserialize(raw)
|
||||||
self.raw = raw
|
self.raw = raw
|
||||||
self.inputs = d['inputs']
|
self.inputs = d['inputs']
|
||||||
self.outputs = map(lambda x: (x['type'], x['address'], x['value']), d['outputs'])
|
self.outputs = [(x['type'], x['address'], x['value']) for x in d['outputs']]
|
||||||
self.locktime = d['lockTime']
|
self.locktime = d['lockTime']
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -546,7 +546,7 @@ class Transaction:
|
||||||
if not inputs:
|
if not inputs:
|
||||||
return
|
return
|
||||||
|
|
||||||
total = sum( map(lambda x:int(x.get('value')), inputs) ) - fee
|
total = sum(i.get('value') for i in inputs) - fee
|
||||||
outputs = [('address', to_address, total)]
|
outputs = [('address', to_address, total)]
|
||||||
self = klass(inputs, outputs)
|
self = klass(inputs, outputs)
|
||||||
self.sign({ pubkey:privkey })
|
self.sign({ pubkey:privkey })
|
||||||
|
@ -567,8 +567,7 @@ class Transaction:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
for k in public_keys:
|
for k in public_keys:
|
||||||
s += op_push(len(k)/2)
|
s += op_push(len(k)/2) + k
|
||||||
s += k
|
|
||||||
if n==2:
|
if n==2:
|
||||||
s += '52'
|
s += '52'
|
||||||
elif n==3:
|
elif n==3:
|
||||||
|
@ -612,8 +611,7 @@ class Transaction:
|
||||||
|
|
||||||
s = int_to_hex(1,4) # version
|
s = int_to_hex(1,4) # version
|
||||||
s += var_int( len(inputs) ) # number of inputs
|
s += var_int( len(inputs) ) # number of inputs
|
||||||
for i in range(len(inputs)):
|
for i, txin in enumerate(inputs):
|
||||||
txin = inputs[i]
|
|
||||||
|
|
||||||
s += txin['prevout_hash'].decode('hex')[::-1].encode('hex') # prev hash
|
s += txin['prevout_hash'].decode('hex')[::-1].encode('hex') # prev hash
|
||||||
s += int_to_hex(txin['prevout_n'],4) # prev index
|
s += int_to_hex(txin['prevout_n'],4) # prev index
|
||||||
|
@ -623,37 +621,31 @@ class Transaction:
|
||||||
address = txin['address']
|
address = txin['address']
|
||||||
|
|
||||||
x_signatures = txin['signatures']
|
x_signatures = txin['signatures']
|
||||||
signatures = filter(lambda x: x is not None, x_signatures)
|
signatures = filter(None, x_signatures)
|
||||||
is_complete = len(signatures) == num_sig
|
is_complete = len(signatures) == num_sig
|
||||||
|
|
||||||
if for_sig in [-1, None]:
|
if for_sig in [-1, None]:
|
||||||
# if we have enough signatures, we use the actual pubkeys
|
# if we have enough signatures, we use the actual pubkeys
|
||||||
# use extended pubkeys (with bip32 derivation)
|
# use extended pubkeys (with bip32 derivation)
|
||||||
sig_list = []
|
|
||||||
if for_sig == -1:
|
if for_sig == -1:
|
||||||
# we assume that signature will be 0x48 bytes long
|
# we assume that signature will be 0x48 bytes long
|
||||||
pubkeys = txin['pubkeys']
|
pubkeys = txin['pubkeys']
|
||||||
sig_list = [ "00" * 0x48 ] * num_sig
|
sig_list = [ "00" * 0x48 ] * num_sig
|
||||||
elif is_complete:
|
elif is_complete:
|
||||||
pubkeys = txin['pubkeys']
|
pubkeys = txin['pubkeys']
|
||||||
for signature in signatures:
|
sig_list = ((sig + '01') for sig in signatures)
|
||||||
sig_list.append(signature + '01')
|
|
||||||
else:
|
else:
|
||||||
pubkeys = txin['x_pubkeys']
|
pubkeys = txin['x_pubkeys']
|
||||||
for signature in x_signatures:
|
sig_list = ((sig + '01') if sig else NO_SIGNATURE for sig in x_signatures)
|
||||||
sig_list.append((signature + '01') if signature is not None else NO_SIGNATURE)
|
script = ''.join(push_script(x) for x in sig_list)
|
||||||
|
|
||||||
sig_list = ''.join( map( lambda x: push_script(x), sig_list))
|
|
||||||
if not p2sh:
|
if not p2sh:
|
||||||
script = sig_list
|
|
||||||
x_pubkey = pubkeys[0]
|
x_pubkey = pubkeys[0]
|
||||||
if x_pubkey is None:
|
if x_pubkey is None:
|
||||||
addrtype, h160 = bc_address_to_hash_160(txin['address'])
|
addrtype, h160 = bc_address_to_hash_160(txin['address'])
|
||||||
x_pubkey = 'fd' + (chr(addrtype) + h160).encode('hex')
|
x_pubkey = 'fd' + (chr(addrtype) + h160).encode('hex')
|
||||||
script += push_script(x_pubkey)
|
script += push_script(x_pubkey)
|
||||||
else:
|
else:
|
||||||
script = '00' # op_0
|
script = '00' + script # put op_0 in front of script
|
||||||
script += sig_list
|
|
||||||
redeem_script = self.multisig_script(pubkeys,2)
|
redeem_script = self.multisig_script(pubkeys,2)
|
||||||
script += push_script(redeem_script)
|
script += push_script(redeem_script)
|
||||||
|
|
||||||
|
@ -689,10 +681,10 @@ class Transaction:
|
||||||
self.raw = None
|
self.raw = None
|
||||||
|
|
||||||
def input_value(self):
|
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):
|
def output_value(self):
|
||||||
return sum([ x[2] for x in self.outputs])
|
return sum( val for tp,addr,val in self.outputs)
|
||||||
|
|
||||||
def get_fee(self):
|
def get_fee(self):
|
||||||
return self.input_value() - self.output_value()
|
return self.input_value() - self.output_value()
|
||||||
|
@ -703,7 +695,7 @@ class Transaction:
|
||||||
for txin in self.inputs:
|
for txin in self.inputs:
|
||||||
if txin.get('is_coinbase'):
|
if txin.get('is_coinbase'):
|
||||||
continue
|
continue
|
||||||
signatures = filter(lambda x: x is not None, txin['signatures'])
|
signatures = filter(None, txin['signatures'])
|
||||||
s += len(signatures)
|
s += len(signatures)
|
||||||
r += txin['num_sig']
|
r += txin['num_sig']
|
||||||
return s, r
|
return s, r
|
||||||
|
@ -716,7 +708,7 @@ class Transaction:
|
||||||
out = set()
|
out = set()
|
||||||
for txin in self.inputs:
|
for txin in self.inputs:
|
||||||
x_signatures = txin['signatures']
|
x_signatures = txin['signatures']
|
||||||
signatures = filter(lambda x: x is not None, x_signatures)
|
signatures = filter(None, x_signatures)
|
||||||
if len(signatures) == txin['num_sig']:
|
if len(signatures) == txin['num_sig']:
|
||||||
# input is complete
|
# input is complete
|
||||||
continue
|
continue
|
||||||
|
@ -730,7 +722,7 @@ class Transaction:
|
||||||
def sign(self, keypairs):
|
def sign(self, keypairs):
|
||||||
print_error("tx.sign(), keypairs:", keypairs)
|
print_error("tx.sign(), keypairs:", keypairs)
|
||||||
for i, txin in enumerate(self.inputs):
|
for i, txin in enumerate(self.inputs):
|
||||||
signatures = filter(lambda x: x is not None, txin['signatures'])
|
signatures = filter(None, txin['signatures'])
|
||||||
num = txin['num_sig']
|
num = txin['num_sig']
|
||||||
if len(signatures) == num:
|
if len(signatures) == num:
|
||||||
# continue if this txin is complete
|
# continue if this txin is complete
|
||||||
|
@ -763,14 +755,14 @@ class Transaction:
|
||||||
self.raw = self.serialize()
|
self.raw = self.serialize()
|
||||||
|
|
||||||
|
|
||||||
def add_pubkey_addresses(self, txlist):
|
def add_pubkey_addresses(self, txdict):
|
||||||
for i in self.inputs:
|
for txin in self.inputs:
|
||||||
if i.get("address") == "(pubkey)":
|
if txin.get('address') == "(pubkey)":
|
||||||
prev_tx = txlist.get(i.get('prevout_hash'))
|
prev_tx = txdict.get(txin.get('prevout_hash'))
|
||||||
if prev_tx:
|
if prev_tx:
|
||||||
address, value = prev_tx.get_outputs()[i.get('prevout_n')]
|
address, value = prev_tx.get_outputs()[txin.get('prevout_n')]
|
||||||
print_error("found pay-to-pubkey address:", address)
|
print_error("found pay-to-pubkey address:", address)
|
||||||
i["address"] = address
|
txin["address"] = address
|
||||||
|
|
||||||
|
|
||||||
def get_outputs(self):
|
def get_outputs(self):
|
||||||
|
@ -788,23 +780,15 @@ class Transaction:
|
||||||
addr = 'OP_RETURN: "' + x.encode('hex') + '"'
|
addr = 'OP_RETURN: "' + x.encode('hex') + '"'
|
||||||
else:
|
else:
|
||||||
addr = "(None)"
|
addr = "(None)"
|
||||||
o.append((addr,v))
|
o.append((addr,v)) # consider using yield (addr, v)
|
||||||
return o
|
return o
|
||||||
|
|
||||||
def get_output_addresses(self):
|
def get_output_addresses(self):
|
||||||
return map(lambda x:x[0], self.get_outputs())
|
return [addr for addr, val in self.get_outputs()]
|
||||||
|
|
||||||
|
|
||||||
def has_address(self, addr):
|
def has_address(self, addr):
|
||||||
found = False
|
return (addr in self.get_output_addresses()) or (addr in (tx.get("address") for tx in self.inputs))
|
||||||
for txin in self.inputs:
|
|
||||||
if addr == txin.get('address'):
|
|
||||||
found = True
|
|
||||||
break
|
|
||||||
if addr in self.get_output_addresses():
|
|
||||||
found = True
|
|
||||||
|
|
||||||
return found
|
|
||||||
|
|
||||||
|
|
||||||
def get_value(self, addresses, prevout_values):
|
def get_value(self, addresses, prevout_values):
|
||||||
|
@ -871,19 +855,22 @@ class Transaction:
|
||||||
|
|
||||||
def requires_fee(self, verifier):
|
def requires_fee(self, verifier):
|
||||||
# see https://en.bitcoin.it/wiki/Transaction_fees
|
# see https://en.bitcoin.it/wiki/Transaction_fees
|
||||||
threshold = 57600000
|
#
|
||||||
|
# size must be smaller than 1 kbyte for free tx
|
||||||
size = len(self.serialize(-1))/2
|
size = len(self.serialize(-1))/2
|
||||||
if size >= 10000:
|
if size >= 10000:
|
||||||
return True
|
return True
|
||||||
|
# all outputs must be 0.01 BTC or larger for free tx
|
||||||
for o in self.get_outputs():
|
for addr, value in self.get_outputs():
|
||||||
value = o[1]
|
|
||||||
if value < 1000000:
|
if value < 1000000:
|
||||||
return True
|
return True
|
||||||
sum = 0
|
# priority must be large enough for free tx
|
||||||
for i in self.inputs:
|
threshold = 57600000
|
||||||
age = verifier.get_confirmations(i["prevout_hash"])[0]
|
weight = 0
|
||||||
sum += i["value"] * age
|
for txin in self.inputs:
|
||||||
priority = sum / size
|
age = verifier.get_confirmations(txin["prevout_hash"])[0]
|
||||||
|
weight += txin["value"] * age
|
||||||
|
priority = weight / size
|
||||||
print_error(priority, threshold)
|
print_error(priority, threshold)
|
||||||
|
|
||||||
return priority < threshold
|
return priority < threshold
|
||||||
|
|
|
@ -329,10 +329,7 @@ class Abstract_Wallet(object):
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
def addresses(self, include_change = True):
|
def addresses(self, include_change = True):
|
||||||
o = []
|
return list(addr for acc in self.accounts for addr in self.get_account_addresses(acc, include_change))
|
||||||
for a in self.accounts.keys():
|
|
||||||
o += self.get_account_addresses(a, include_change)
|
|
||||||
return o
|
|
||||||
|
|
||||||
def is_mine(self, address):
|
def is_mine(self, address):
|
||||||
return address in self.addresses(True)
|
return address in self.addresses(True)
|
||||||
|
@ -344,12 +341,11 @@ class Abstract_Wallet(object):
|
||||||
return s[0] == 1
|
return s[0] == 1
|
||||||
|
|
||||||
def get_address_index(self, address):
|
def get_address_index(self, address):
|
||||||
for account in self.accounts.keys():
|
for acc_id in self.accounts:
|
||||||
for for_change in [0,1]:
|
for for_change in [0,1]:
|
||||||
addresses = self.accounts[account].get_addresses(for_change)
|
addresses = self.accounts[acc_id].get_addresses(for_change)
|
||||||
for addr in addresses:
|
if address in addresses:
|
||||||
if address == addr:
|
return acc_id, (for_change, addresses.index(address))
|
||||||
return account, (for_change, addresses.index(addr))
|
|
||||||
raise Exception("Address not found", address)
|
raise Exception("Address not found", address)
|
||||||
|
|
||||||
def get_private_key(self, address, password):
|
def get_private_key(self, address, password):
|
||||||
|
@ -394,7 +390,8 @@ class Abstract_Wallet(object):
|
||||||
self.storage.put('addressbook', self.addressbook, True)
|
self.storage.put('addressbook', self.addressbook, True)
|
||||||
|
|
||||||
def fill_addressbook(self):
|
def fill_addressbook(self):
|
||||||
for tx_hash, tx in self.transactions.items():
|
# todo: optimize this
|
||||||
|
for tx_hash, tx in self.transactions.viewitems():
|
||||||
is_relevant, is_send, _, _ = self.get_tx_value(tx)
|
is_relevant, is_send, _, _ = self.get_tx_value(tx)
|
||||||
if is_send:
|
if is_send:
|
||||||
for addr in tx.get_output_addresses():
|
for addr in tx.get_output_addresses():
|
||||||
|
@ -426,12 +423,13 @@ class Abstract_Wallet(object):
|
||||||
self.spent_outputs.append(key)
|
self.spent_outputs.append(key)
|
||||||
|
|
||||||
def get_addr_balance(self, address):
|
def get_addr_balance(self, address):
|
||||||
|
'returns the confirmed balance and pending (unconfirmed) balance change of this bitcoin address'
|
||||||
#assert self.is_mine(address)
|
#assert self.is_mine(address)
|
||||||
h = self.history.get(address,[])
|
h = self.history.get(address,[])
|
||||||
if h == ['*']: return 0,0
|
if h == ['*']: return 0,0
|
||||||
c = u = 0
|
c = u = 0
|
||||||
received_coins = [] # list of coins received at address
|
received_coins = [] # list of coins received at address
|
||||||
|
# go through all tx in history of this address and collect the coins arriving on this address
|
||||||
for tx_hash, tx_height in h:
|
for tx_hash, tx_height in h:
|
||||||
tx = self.transactions.get(tx_hash)
|
tx = self.transactions.get(tx_hash)
|
||||||
if not tx: continue
|
if not tx: continue
|
||||||
|
@ -440,12 +438,12 @@ class Abstract_Wallet(object):
|
||||||
if addr == address:
|
if addr == address:
|
||||||
key = tx_hash + ':%d'%i
|
key = tx_hash + ':%d'%i
|
||||||
received_coins.append(key)
|
received_coins.append(key)
|
||||||
|
# go through all tx in history of this address again
|
||||||
for tx_hash, tx_height in h:
|
for tx_hash, tx_height in h:
|
||||||
tx = self.transactions.get(tx_hash)
|
tx = self.transactions.get(tx_hash)
|
||||||
if not tx: continue
|
if not tx: continue
|
||||||
v = 0
|
v = 0
|
||||||
|
# substract the value of coins leaving from this address
|
||||||
for item in tx.inputs:
|
for item in tx.inputs:
|
||||||
addr = item.get('address')
|
addr = item.get('address')
|
||||||
if addr == address:
|
if addr == address:
|
||||||
|
@ -453,16 +451,16 @@ class Abstract_Wallet(object):
|
||||||
value = self.prevout_values.get( key )
|
value = self.prevout_values.get( key )
|
||||||
if key in received_coins:
|
if key in received_coins:
|
||||||
v -= value
|
v -= value
|
||||||
|
# add the value of the coins arriving in this address
|
||||||
for i, (addr, value) in enumerate(tx.get_outputs()):
|
for i, (addr, value) in enumerate(tx.get_outputs()):
|
||||||
key = tx_hash + ':%d'%i
|
key = tx_hash + ':%d'%i
|
||||||
if addr == address:
|
if addr == address:
|
||||||
v += value
|
v += value
|
||||||
|
|
||||||
if tx_height:
|
if tx_height:
|
||||||
c += v
|
c += v # confirmed coins value
|
||||||
else:
|
else:
|
||||||
u += v
|
u += v # unconfirmed coins value
|
||||||
return c, u
|
return c, u
|
||||||
|
|
||||||
def get_account_name(self, k):
|
def get_account_name(self, k):
|
||||||
|
@ -474,14 +472,22 @@ class Abstract_Wallet(object):
|
||||||
account_names[k] = self.get_account_name(k)
|
account_names[k] = self.get_account_name(k)
|
||||||
return account_names
|
return account_names
|
||||||
|
|
||||||
def get_account_addresses(self, a, include_change=True):
|
def get_account_addresses(self, acc_id, include_change=True):
|
||||||
if a is None:
|
if acc_id is None:
|
||||||
o = self.addresses(include_change)
|
addr_list = self.addresses(include_change)
|
||||||
elif a in self.accounts:
|
elif acc_id in self.accounts:
|
||||||
ac = self.accounts[a]
|
acc = self.accounts[acc_id]
|
||||||
o = ac.get_addresses(0)
|
addr_list = acc.get_addresses(0)
|
||||||
if include_change: o += ac.get_addresses(1)
|
if include_change:
|
||||||
return o
|
addr_list += acc.get_addresses(1)
|
||||||
|
return addr_list
|
||||||
|
|
||||||
|
def get_account_from_address(self, addr):
|
||||||
|
"Returns the account that contains this address, or None"
|
||||||
|
for acc_id in self.accounts: # similar to get_address_index but simpler
|
||||||
|
if addr in self.get_account_addresses(acc_id):
|
||||||
|
return self.accounts[acc_id]
|
||||||
|
return None
|
||||||
|
|
||||||
def get_account_balance(self, account):
|
def get_account_balance(self, account):
|
||||||
return self.get_balance(self.get_account_addresses(account))
|
return self.get_balance(self.get_account_addresses(account))
|
||||||
|
@ -524,7 +530,7 @@ class Abstract_Wallet(object):
|
||||||
if coins[-1][0] != 0:
|
if coins[-1][0] != 0:
|
||||||
while coins[0][0] == 0:
|
while coins[0][0] == 0:
|
||||||
coins = coins[1:] + [ coins[0] ]
|
coins = coins[1:] + [ coins[0] ]
|
||||||
return [x[1] for x in coins]
|
return [value for height, value in coins]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue