mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-31 17:31:36 +00:00
wallet: randomise locktime of transactions a bit. also check if stale. (#4967)
This commit is contained in:
parent
d5c8a0e0d0
commit
dc19cf1fa1
3 changed files with 33 additions and 7 deletions
|
@ -426,6 +426,11 @@ class Blockchain(util.PrintError):
|
|||
return None
|
||||
return deserialize_header(h, height)
|
||||
|
||||
def header_at_tip(self) -> Optional[dict]:
|
||||
"""Return latest header."""
|
||||
height = self.height()
|
||||
return self.read_header(height)
|
||||
|
||||
def get_hash(self, height: int) -> str:
|
||||
def is_height_checkpoint():
|
||||
within_cp_range = height <= constants.net.max_checkpoint()
|
||||
|
|
|
@ -1033,7 +1033,6 @@ class TestWalletSending(TestCaseForTestnet):
|
|||
|
||||
class NetworkMock:
|
||||
relay_fee = 1000
|
||||
def get_local_height(self): return 1325785
|
||||
def run_from_another_thread(self, coro):
|
||||
loop = asyncio.get_event_loop()
|
||||
return loop.run_until_complete(coro)
|
||||
|
@ -1046,7 +1045,7 @@ class TestWalletSending(TestCaseForTestnet):
|
|||
privkeys = ['93NQ7CFbwTPyKDJLXe97jczw33fiLijam2SCZL3Uinz1NSbHrTu', ]
|
||||
network = NetworkMock()
|
||||
dest_addr = 'tb1q3ws2p0qjk5vrravv065xqlnkckvzcpclk79eu2'
|
||||
tx = sweep(privkeys, network, config=None, recipient=dest_addr, fee=5000)
|
||||
tx = sweep(privkeys, network, config=None, recipient=dest_addr, fee=5000, locktime=1325785)
|
||||
|
||||
tx_copy = Transaction(tx.serialize())
|
||||
self.assertEqual('010000000129349e5641d79915e9d0282fdbaee8c3df0b6731bab9d70bf626e8588bde24ac010000004847304402206bf0d0a93abae0d5873a62ebf277a5dd2f33837821e8b93e74d04e19d71b578002201a6d729bc159941ef5c4c9e5fe13ece9fc544351ba531b00f68ba549c8b38a9a01fdffffff01b82e0f00000000001600148ba0a0bc12b51831f58c7ea8607e76c5982c071fd93a1400',
|
||||
|
|
|
@ -125,7 +125,8 @@ def sweep_preparations(privkeys, network: 'Network', imax=100):
|
|||
return inputs, keypairs
|
||||
|
||||
|
||||
def sweep(privkeys, network: 'Network', config: 'SimpleConfig', recipient, fee=None, imax=100):
|
||||
def sweep(privkeys, network: 'Network', config: 'SimpleConfig', recipient, fee=None, imax=100,
|
||||
*, locktime=None):
|
||||
inputs, keypairs = sweep_preparations(privkeys, network, imax)
|
||||
total = sum(i.get('value') for i in inputs)
|
||||
if fee is None:
|
||||
|
@ -138,7 +139,8 @@ def sweep(privkeys, network: 'Network', config: 'SimpleConfig', recipient, fee=N
|
|||
raise Exception(_('Not enough funds on address.') + '\nTotal: %d satoshis\nFee: %d\nDust Threshold: %d'%(total, fee, dust_threshold(network)))
|
||||
|
||||
outputs = [TxOutput(TYPE_ADDRESS, recipient, total - fee)]
|
||||
locktime = network.get_local_height()
|
||||
if locktime is None:
|
||||
locktime = get_locktime_for_new_transaction(network)
|
||||
|
||||
tx = Transaction.from_io(inputs, outputs, locktime=locktime)
|
||||
tx.set_rbf(True)
|
||||
|
@ -146,6 +148,26 @@ def sweep(privkeys, network: 'Network', config: 'SimpleConfig', recipient, fee=N
|
|||
return tx
|
||||
|
||||
|
||||
def get_locktime_for_new_transaction(network: 'Network') -> int:
|
||||
# if no network or not up to date, just set locktime to zero
|
||||
if not network:
|
||||
return 0
|
||||
chain = network.blockchain()
|
||||
header = chain.header_at_tip()
|
||||
if not header:
|
||||
return 0
|
||||
STALE_DELAY = 8 * 60 * 60 # in seconds
|
||||
if header['timestamp'] + STALE_DELAY < time.time():
|
||||
return 0
|
||||
# discourage "fee sniping"
|
||||
locktime = chain.height()
|
||||
# sometimes pick locktime a bit further back, to help privacy
|
||||
# of setups that need more time (offline/multisig/coinjoin/...)
|
||||
if random.randint(0, 9) == 0:
|
||||
locktime = max(0, locktime - random.randint(0, 99))
|
||||
return locktime
|
||||
|
||||
|
||||
|
||||
class CannotBumpFee(Exception): pass
|
||||
|
||||
|
@ -692,7 +714,7 @@ class Abstract_Wallet(AddressSynchronizer):
|
|||
tx = Transaction.from_io(coins, outputs[:])
|
||||
|
||||
# Timelock tx to current height.
|
||||
tx.locktime = self.get_local_height()
|
||||
tx.locktime = get_locktime_for_new_transaction(self.network)
|
||||
run_hook('make_unsigned_transaction', self, tx)
|
||||
return tx
|
||||
|
||||
|
@ -794,7 +816,7 @@ class Abstract_Wallet(AddressSynchronizer):
|
|||
continue
|
||||
if delta > 0:
|
||||
raise CannotBumpFee(_('Cannot bump fee') + ': ' + _('could not find suitable outputs'))
|
||||
locktime = self.get_local_height()
|
||||
locktime = get_locktime_for_new_transaction(self.network)
|
||||
tx_new = Transaction.from_io(inputs, outputs, locktime=locktime)
|
||||
return tx_new
|
||||
|
||||
|
@ -814,7 +836,7 @@ class Abstract_Wallet(AddressSynchronizer):
|
|||
inputs = [item]
|
||||
out_address = self.get_unused_address() or address
|
||||
outputs = [TxOutput(TYPE_ADDRESS, out_address, value - fee)]
|
||||
locktime = self.get_local_height()
|
||||
locktime = get_locktime_for_new_transaction(self.network)
|
||||
return Transaction.from_io(inputs, outputs, locktime=locktime)
|
||||
|
||||
def add_input_sig_info(self, txin, address):
|
||||
|
|
Loading…
Add table
Reference in a new issue