84 lines
2.4 KiB
Python
84 lines
2.4 KiB
Python
|
import logging
|
||
|
|
||
|
from lbryschema.address import public_key_to_address
|
||
|
|
||
|
from .lbrycrd import deserialize_xkey
|
||
|
from .lbrycrd import CKD_pub
|
||
|
|
||
|
log = logging.getLogger(__name__)
|
||
|
|
||
|
|
||
|
def get_key_chain_from_xpub(xpub):
|
||
|
_, _, _, chain, key = deserialize_xkey(xpub)
|
||
|
return key, chain
|
||
|
|
||
|
|
||
|
def derive_key(parent_key, chain, sequence):
|
||
|
return CKD_pub(parent_key, chain, sequence)[0]
|
||
|
|
||
|
|
||
|
class AddressSequence:
|
||
|
|
||
|
def __init__(self, derived_keys, gap, age_checker, pub_key, chain_key):
|
||
|
self.gap = gap
|
||
|
self.is_old = age_checker
|
||
|
self.pub_key = pub_key
|
||
|
self.chain_key = chain_key
|
||
|
self.derived_keys = derived_keys
|
||
|
self.addresses = [
|
||
|
public_key_to_address(key.decode('hex'))
|
||
|
for key in derived_keys
|
||
|
]
|
||
|
|
||
|
def generate_next_address(self):
|
||
|
new_key, _ = derive_key(self.pub_key, self.chain_key, len(self.derived_keys))
|
||
|
address = public_key_to_address(new_key)
|
||
|
self.derived_keys.append(new_key.encode('hex'))
|
||
|
self.addresses.append(address)
|
||
|
return address
|
||
|
|
||
|
def has_gap(self):
|
||
|
if len(self.addresses) < self.gap:
|
||
|
return False
|
||
|
for address in self.addresses[-self.gap:]:
|
||
|
if self.is_old(address):
|
||
|
return False
|
||
|
return True
|
||
|
|
||
|
def ensure_enough_addresses(self):
|
||
|
starting_length = len(self.addresses)
|
||
|
while not self.has_gap():
|
||
|
self.generate_next_address()
|
||
|
return self.addresses[starting_length:]
|
||
|
|
||
|
|
||
|
class Account:
|
||
|
|
||
|
def __init__(self, data, receiving_gap, change_gap, age_checker):
|
||
|
self.xpub = data['xpub']
|
||
|
master_key, master_chain = get_key_chain_from_xpub(data['xpub'])
|
||
|
self.receiving = AddressSequence(
|
||
|
data.get('receiving', []), receiving_gap, age_checker,
|
||
|
*derive_key(master_key, master_chain, 0)
|
||
|
)
|
||
|
self.change = AddressSequence(
|
||
|
data.get('change', []), change_gap, age_checker,
|
||
|
*derive_key(master_key, master_chain, 1)
|
||
|
)
|
||
|
self.is_old = age_checker
|
||
|
|
||
|
def as_dict(self):
|
||
|
return {
|
||
|
'receiving': self.receiving.derived_keys,
|
||
|
'change': self.change.derived_keys,
|
||
|
'xpub': self.xpub
|
||
|
}
|
||
|
|
||
|
def ensure_enough_addresses(self):
|
||
|
return self.receiving.ensure_enough_addresses() + \
|
||
|
self.change.ensure_enough_addresses()
|
||
|
|
||
|
@property
|
||
|
def sequences(self):
|
||
|
return self.receiving, self.change
|