|
|
@ -195,6 +195,9 @@ class Abstract_Wallet(AddressSynchronizer): |
|
|
|
if storage.requires_upgrade(): |
|
|
|
raise Exception("storage must be upgraded before constructing wallet") |
|
|
|
|
|
|
|
# load addresses needs to be called before constructor for sanity checks |
|
|
|
storage.db.load_addresses(self.wallet_type) |
|
|
|
|
|
|
|
AddressSynchronizer.__init__(self, storage) |
|
|
|
|
|
|
|
# saved fields |
|
|
@ -219,7 +222,6 @@ class Abstract_Wallet(AddressSynchronizer): |
|
|
|
|
|
|
|
def load_and_cleanup(self): |
|
|
|
self.load_keystore() |
|
|
|
self.load_addresses() |
|
|
|
self.test_addresses_sanity() |
|
|
|
super().load_and_cleanup() |
|
|
|
|
|
|
@ -235,15 +237,6 @@ class Abstract_Wallet(AddressSynchronizer): |
|
|
|
def basename(self): |
|
|
|
return os.path.basename(self.storage.path) |
|
|
|
|
|
|
|
def save_addresses(self): |
|
|
|
self.storage.put('addresses', {'receiving':self.receiving_addresses, 'change':self.change_addresses}) |
|
|
|
|
|
|
|
def load_addresses(self): |
|
|
|
d = self.storage.get('addresses', {}) |
|
|
|
if type(d) != dict: d={} |
|
|
|
self.receiving_addresses = d.get('receiving', []) |
|
|
|
self.change_addresses = d.get('change', []) |
|
|
|
|
|
|
|
def test_addresses_sanity(self): |
|
|
|
addrs = self.get_receiving_addresses() |
|
|
|
if len(addrs) > 0: |
|
|
@ -320,11 +313,7 @@ class Abstract_Wallet(AddressSynchronizer): |
|
|
|
return |
|
|
|
|
|
|
|
def is_mine(self, address): |
|
|
|
try: |
|
|
|
self.get_address_index(address) |
|
|
|
except KeyError: |
|
|
|
return False |
|
|
|
return True |
|
|
|
return bool(self.get_address_index(address)) |
|
|
|
|
|
|
|
def is_change(self, address): |
|
|
|
if not self.is_mine(address): |
|
|
@ -1127,12 +1116,7 @@ class Abstract_Wallet(AddressSynchronizer): |
|
|
|
return True |
|
|
|
|
|
|
|
def get_sorted_requests(self, config): |
|
|
|
def f(addr): |
|
|
|
try: |
|
|
|
return self.get_address_index(addr) |
|
|
|
except: |
|
|
|
return |
|
|
|
keys = map(lambda x: (f(x), x), self.receive_requests.keys()) |
|
|
|
keys = map(lambda x: (self.get_address_index(x), x), self.receive_requests.keys()) |
|
|
|
sorted_keys = sorted(filter(lambda x: x[0] is not None, keys)) |
|
|
|
return [self.get_payment_request(x[1], config) for x in sorted_keys] |
|
|
|
|
|
|
@ -1327,18 +1311,12 @@ class Imported_Wallet(Simple_Wallet): |
|
|
|
|
|
|
|
def load_keystore(self): |
|
|
|
self.keystore = load_keystore(self.storage, 'keystore') if self.storage.get('keystore') else None |
|
|
|
|
|
|
|
def save_keystore(self): |
|
|
|
self.storage.put('keystore', self.keystore.dump()) |
|
|
|
|
|
|
|
def load_addresses(self): |
|
|
|
self.addresses = self.storage.get('addresses', {}) |
|
|
|
# fixme: a reference to addresses is needed |
|
|
|
if self.keystore: |
|
|
|
self.keystore.addresses = self.addresses |
|
|
|
self.keystore.addresses = self.db.imported_addresses |
|
|
|
|
|
|
|
def save_addresses(self): |
|
|
|
self.storage.put('addresses', self.addresses) |
|
|
|
def save_keystore(self): |
|
|
|
self.storage.put('keystore', self.keystore.dump()) |
|
|
|
|
|
|
|
def can_import_address(self): |
|
|
|
return self.is_watching_only() |
|
|
@ -1366,7 +1344,7 @@ class Imported_Wallet(Simple_Wallet): |
|
|
|
|
|
|
|
def get_addresses(self): |
|
|
|
# note: overridden so that the history can be cleared |
|
|
|
return sorted(self.addresses.keys()) |
|
|
|
return self.db.get_imported_addresses() |
|
|
|
|
|
|
|
def get_receiving_addresses(self): |
|
|
|
return self.get_addresses() |
|
|
@ -1382,13 +1360,14 @@ class Imported_Wallet(Simple_Wallet): |
|
|
|
if not bitcoin.is_address(address): |
|
|
|
bad_addr.append((address, _('invalid address'))) |
|
|
|
continue |
|
|
|
if address in self.addresses: |
|
|
|
if self.db.has_imported_address(address): |
|
|
|
bad_addr.append((address, _('address already in wallet'))) |
|
|
|
continue |
|
|
|
good_addr.append(address) |
|
|
|
self.addresses[address] = {} |
|
|
|
self.db.add_imported_address(address, {}) |
|
|
|
self.add_address(address) |
|
|
|
self.save_addresses() |
|
|
|
if write_to_disk: |
|
|
|
self.storage.write() |
|
|
|
return good_addr, bad_addr |
|
|
|
|
|
|
|
def import_address(self, address: str) -> str: |
|
|
@ -1399,7 +1378,7 @@ class Imported_Wallet(Simple_Wallet): |
|
|
|
raise BitcoinException(str(bad_addr[0][1])) |
|
|
|
|
|
|
|
def delete_address(self, address): |
|
|
|
if address not in self.addresses: |
|
|
|
if not self.db.has_imported_address(address): |
|
|
|
return |
|
|
|
transactions_to_remove = set() # only referred to by this address |
|
|
|
transactions_new = set() # txs that are not only referred to by address |
|
|
@ -1424,7 +1403,7 @@ class Imported_Wallet(Simple_Wallet): |
|
|
|
self.remove_payment_request(address, {}) |
|
|
|
self.set_frozen_state([address], False) |
|
|
|
pubkey = self.get_public_key(address) |
|
|
|
self.addresses.pop(address) |
|
|
|
self.db.remove_imported_address(address) |
|
|
|
if pubkey: |
|
|
|
# delete key iff no other address uses it (e.g. p2pkh and p2wpkh for same key) |
|
|
|
for txin_type in bitcoin.WIF_SCRIPT_TYPES.keys(): |
|
|
@ -1433,19 +1412,23 @@ class Imported_Wallet(Simple_Wallet): |
|
|
|
except NotImplementedError: |
|
|
|
pass |
|
|
|
else: |
|
|
|
if addr2 in self.addresses: |
|
|
|
if self.db.has_imported_address(addr2): |
|
|
|
break |
|
|
|
else: |
|
|
|
self.keystore.delete_imported_key(pubkey) |
|
|
|
self.save_keystore() |
|
|
|
self.save_addresses() |
|
|
|
self.storage.write() |
|
|
|
|
|
|
|
def is_mine(self, address): |
|
|
|
return self.db.has_imported_address(address) |
|
|
|
|
|
|
|
def get_address_index(self, address): |
|
|
|
# returns None is address is not mine |
|
|
|
return self.get_public_key(address) |
|
|
|
|
|
|
|
def get_public_key(self, address): |
|
|
|
return self.addresses[address].get('pubkey') |
|
|
|
x = self.db.get_imported_address(address) |
|
|
|
return x.get('pubkey') if x else None |
|
|
|
|
|
|
|
def import_private_keys(self, keys: List[str], password: Optional[str], *, |
|
|
|
write_to_disk=True) -> Tuple[List[str], List[Tuple[str, str]]]: |
|
|
@ -1462,10 +1445,9 @@ class Imported_Wallet(Simple_Wallet): |
|
|
|
continue |
|
|
|
addr = bitcoin.pubkey_to_address(txin_type, pubkey) |
|
|
|
good_addr.append(addr) |
|
|
|
self.addresses[addr] = {'type':txin_type, 'pubkey':pubkey, 'redeem_script':None} |
|
|
|
self.db.add_imported_address(addr, {'type':txin_type, 'pubkey':pubkey, 'redeem_script':None}) |
|
|
|
self.add_address(addr) |
|
|
|
self.save_keystore() |
|
|
|
self.save_addresses() |
|
|
|
if write_to_disk: |
|
|
|
self.storage.write() |
|
|
|
return good_addr, bad_keys |
|
|
@ -1478,12 +1460,12 @@ class Imported_Wallet(Simple_Wallet): |
|
|
|
raise BitcoinException(str(bad_keys[0][1])) |
|
|
|
|
|
|
|
def get_redeem_script(self, address): |
|
|
|
d = self.addresses[address] |
|
|
|
d = self.db.get_imported_address(address) |
|
|
|
redeem_script = d['redeem_script'] |
|
|
|
return redeem_script |
|
|
|
|
|
|
|
def get_txin_type(self, address): |
|
|
|
return self.addresses[address].get('type', 'address') |
|
|
|
return self.db.get_imported_address(address).get('type', 'address') |
|
|
|
|
|
|
|
def add_input_sig_info(self, txin, address): |
|
|
|
if self.is_watching_only(): |
|
|
@ -1492,7 +1474,7 @@ class Imported_Wallet(Simple_Wallet): |
|
|
|
txin['signatures'] = [None] |
|
|
|
return |
|
|
|
if txin['type'] in ['p2pkh', 'p2wpkh', 'p2wpkh-p2sh']: |
|
|
|
pubkey = self.addresses[address]['pubkey'] |
|
|
|
pubkey = self.db.get_imported_address(address)['pubkey'] |
|
|
|
txin['num_sig'] = 1 |
|
|
|
txin['x_pubkeys'] = [pubkey] |
|
|
|
txin['signatures'] = [None] |
|
|
@ -1500,8 +1482,8 @@ class Imported_Wallet(Simple_Wallet): |
|
|
|
raise NotImplementedError('imported wallets for p2sh are not implemented') |
|
|
|
|
|
|
|
def pubkeys_to_address(self, pubkey): |
|
|
|
for addr, v in self.addresses.items(): |
|
|
|
if v.get('pubkey') == pubkey: |
|
|
|
for addr in self.db.get_imported_addresses(): |
|
|
|
if self.db.get_imported_address(addr)['pubkey'] == pubkey: |
|
|
|
return addr |
|
|
|
|
|
|
|
class Deterministic_Wallet(Abstract_Wallet): |
|
|
@ -1525,10 +1507,10 @@ class Deterministic_Wallet(Abstract_Wallet): |
|
|
|
return out |
|
|
|
|
|
|
|
def get_receiving_addresses(self): |
|
|
|
return self.receiving_addresses |
|
|
|
return self.db.get_receiving_addresses() |
|
|
|
|
|
|
|
def get_change_addresses(self): |
|
|
|
return self.change_addresses |
|
|
|
return self.db.get_change_addresses() |
|
|
|
|
|
|
|
@profiler |
|
|
|
def try_detecting_internal_addresses_corruption(self): |
|
|
@ -1557,18 +1539,10 @@ class Deterministic_Wallet(Abstract_Wallet): |
|
|
|
|
|
|
|
def change_gap_limit(self, value): |
|
|
|
'''This method is not called in the code, it is kept for console use''' |
|
|
|
if value >= self.gap_limit: |
|
|
|
if value >= self.min_acceptable_gap(): |
|
|
|
self.gap_limit = value |
|
|
|
self.storage.put('gap_limit', self.gap_limit) |
|
|
|
return True |
|
|
|
elif value >= self.min_acceptable_gap(): |
|
|
|
addresses = self.get_receiving_addresses() |
|
|
|
k = self.num_unused_trailing_addresses(addresses) |
|
|
|
n = len(addresses) - k + value |
|
|
|
self.receiving_addresses = self.receiving_addresses[0:n] |
|
|
|
self.gap_limit = value |
|
|
|
self.storage.put('gap_limit', self.gap_limit) |
|
|
|
self.save_addresses() |
|
|
|
self.storage.write() |
|
|
|
return True |
|
|
|
else: |
|
|
|
return False |
|
|
@ -1595,14 +1569,6 @@ class Deterministic_Wallet(Abstract_Wallet): |
|
|
|
nmax = max(nmax, n) |
|
|
|
return nmax + 1 |
|
|
|
|
|
|
|
def load_addresses(self): |
|
|
|
super().load_addresses() |
|
|
|
self._addr_to_addr_index = {} # key: address, value: (is_change, index) |
|
|
|
for i, addr in enumerate(self.receiving_addresses): |
|
|
|
self._addr_to_addr_index[addr] = (False, i) |
|
|
|
for i, addr in enumerate(self.change_addresses): |
|
|
|
self._addr_to_addr_index[addr] = (True, i) |
|
|
|
|
|
|
|
def derive_address(self, for_change, n): |
|
|
|
x = self.derive_pubkeys(for_change, n) |
|
|
|
return self.pubkeys_to_address(x) |
|
|
@ -1610,12 +1576,9 @@ class Deterministic_Wallet(Abstract_Wallet): |
|
|
|
def create_new_address(self, for_change=False): |
|
|
|
assert type(for_change) is bool |
|
|
|
with self.lock: |
|
|
|
addr_list = self.change_addresses if for_change else self.receiving_addresses |
|
|
|
n = len(addr_list) |
|
|
|
n = self.db.num_change_addresses() if for_change else self.db.num_receiving_addresses() |
|
|
|
address = self.derive_address(for_change, n) |
|
|
|
addr_list.append(address) |
|
|
|
self._addr_to_addr_index[address] = (for_change, n) |
|
|
|
self.save_addresses() |
|
|
|
self.db.add_change_address(address) if for_change else self.db.add_receiving_address(address) |
|
|
|
self.add_address(address) |
|
|
|
if for_change: |
|
|
|
# note: if it's actually used, it will get filtered later |
|
|
@ -1652,7 +1615,7 @@ class Deterministic_Wallet(Abstract_Wallet): |
|
|
|
return True |
|
|
|
|
|
|
|
def get_address_index(self, address): |
|
|
|
return self._addr_to_addr_index[address] |
|
|
|
return self.db.get_address_index(address) |
|
|
|
|
|
|
|
def get_master_public_keys(self): |
|
|
|
return [self.get_master_public_key()] |
|
|
|