Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wallet: make gap limit for change configurable #8726

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions electrum/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -974,7 +974,7 @@ async def createnewaddress(self, wallet: Abstract_Wallet = None):
return wallet.create_new_address(False)

@command('w')
async def changegaplimit(self, new_limit, iknowwhatimdoing=False, wallet: Abstract_Wallet = None):
async def changegaplimit(self, new_limit, change=False, iknowwhatimdoing=False, wallet: Abstract_Wallet = None):
"""Change the gap limit of the wallet."""
if not iknowwhatimdoing:
raise Exception("WARNING: Are you SURE you want to change the gap limit?\n"
Expand All @@ -984,18 +984,20 @@ async def changegaplimit(self, new_limit, iknowwhatimdoing=False, wallet: Abstra
"To proceed, try again, with the --iknowwhatimdoing option.")
if not isinstance(wallet, Deterministic_Wallet):
raise Exception("This wallet is not deterministic.")
if change:
return wallet.change_gap_limit_for_change(new_limit)
return wallet.change_gap_limit(new_limit)

@command('wn')
async def getminacceptablegap(self, wallet: Abstract_Wallet = None):
async def getminacceptablegap(self, change=False, wallet: Abstract_Wallet = None):
"""Returns the minimum value for gap limit that would be sufficient to discover all
known addresses in the wallet.
"""
if not isinstance(wallet, Deterministic_Wallet):
raise Exception("This wallet is not deterministic.")
if not wallet.is_up_to_date():
raise NotSynchronizedException("Wallet not fully synchronized.")
return wallet.min_acceptable_gap()
return wallet.min_acceptable_gap(change)

@command('w')
async def getunusedaddress(self, wallet: Abstract_Wallet = None):
Expand Down
38 changes: 33 additions & 5 deletions electrum/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,6 @@ class Abstract_Wallet(ABC, Logger, EventListener):

LOGGING_SHORTCUT = 'w'
max_change_outputs = 3
gap_limit_for_change = 10

txin_type: str
wallet_type: str
Expand Down Expand Up @@ -3462,6 +3461,7 @@ def __init__(self, db, *, config):
self._ephemeral_addr_to_addr_index = {} # type: Dict[str, Sequence[int]]
Abstract_Wallet.__init__(self, db, config=config)
self.gap_limit = db.get('gap_limit', 20)
self.gap_limit_for_change = db.get('gap_limit_for_change', 10)
# generate addresses now. note that without libsecp this might block
# for a few seconds!
self.synchronize()
Expand Down Expand Up @@ -3523,6 +3523,17 @@ def change_gap_limit(self, value):
else:
return False

def change_gap_limit_for_change(self, value):
'''This method is not called in the code, it is kept for console use'''
value = int(value)
if value >= self.min_acceptable_gap(True):
self.gap_limit_for_change = value
self.db.put('gap_limit_for_change', self.gap_limit_for_change)
self.save_db()
return True
else:
return False

def num_unused_trailing_addresses(self, addresses):
k = 0
for addr in addresses[::-1]:
Expand All @@ -3531,11 +3542,14 @@ def num_unused_trailing_addresses(self, addresses):
k += 1
return k

def min_acceptable_gap(self) -> int:
def min_acceptable_gap(self, for_change: bool = False) -> int:
# fixme: this assumes wallet is synchronized
n = 0
nmax = 0
addresses = self.get_receiving_addresses()
if for_change:
addresses = self.get_change_addresses()
else:
addresses = self.get_receiving_addresses()
k = self.num_unused_trailing_addresses(addresses)
for addr in addresses[0:-k]:
if self.adb.address_is_old(addr):
Expand Down Expand Up @@ -3829,8 +3843,17 @@ def wallet_class(wallet_type):
raise WalletFileException("Unknown wallet type: " + str(wallet_type))


def create_new_wallet(*, path, config: SimpleConfig, passphrase=None, password=None,
encrypt_file=True, seed_type=None, gap_limit=None) -> dict:
def create_new_wallet(
*,
path,
config: SimpleConfig,
passphrase=None,
password=None,
encrypt_file=True,
seed_type=None,
gap_limit=None,
gap_limit_for_change=None
) -> dict:
"""Create a new wallet"""
storage = WalletStorage(path)
if storage.file_exists():
Expand All @@ -3845,6 +3868,8 @@ def create_new_wallet(*, path, config: SimpleConfig, passphrase=None, password=N
db.put('lightning_xprv', k.get_lightning_xprv(None))
if gap_limit is not None:
db.put('gap_limit', gap_limit)
if gap_limit_for_change is not None:
db.put('gap_limit_for_change', gap_limit_for_change)
wallet = Wallet(db, config=config)
wallet.update_password(old_pw=None, new_pw=password, encrypt_storage=encrypt_file)
wallet.synchronize()
Expand All @@ -3862,6 +3887,7 @@ def restore_wallet_from_text(
password: Optional[str] = None,
encrypt_file: Optional[bool] = None,
gap_limit: Optional[int] = None,
gap_limit_for_change: Optional[int] = None,
) -> dict:
"""Restore a wallet from text. Text can be a seed phrase, a master
public key, a master private key, a list of bitcoin addresses
Expand Down Expand Up @@ -3905,6 +3931,8 @@ def restore_wallet_from_text(
db.put('wallet_type', 'standard')
if gap_limit is not None:
db.put('gap_limit', gap_limit)
if gap_limit_for_change is not None:
db.put('gap_limit_for_change', gap_limit_for_change)
wallet = Wallet(db, config=config)
if db.storage:
assert not db.storage.file_exists(), "file was created too soon! plaintext keys might have been written to disk"
Expand Down