Browse Source

Privacy improvements for direct_send()

* Shuffle order of tx inputs and outputs
* Set locktime for best anonset (Core, Electrum)
master
Kristaps Kaupe 6 years ago
parent
commit
c23808f054
No known key found for this signature in database
GPG Key ID: D47B1B4232B55437
  1. 2
      jmclient/jmclient/__init__.py
  2. 4
      jmclient/jmclient/blockchaininterface.py
  3. 8
      jmclient/jmclient/maker.py
  4. 8
      jmclient/jmclient/taker.py
  5. 8
      jmclient/jmclient/taker_utils.py
  6. 11
      jmclient/jmclient/wallet.py

2
jmclient/jmclient/__init__.py

@ -17,7 +17,7 @@ from .taker import Taker, P2EPTaker
from .wallet import (Mnemonic, estimate_tx_fee, WalletError, BaseWallet, ImportWalletMixin, from .wallet import (Mnemonic, estimate_tx_fee, WalletError, BaseWallet, ImportWalletMixin,
BIP39WalletMixin, BIP32Wallet, BIP49Wallet, LegacyWallet, BIP39WalletMixin, BIP32Wallet, BIP49Wallet, LegacyWallet,
SegwitWallet, SegwitLegacyWallet, UTXOManager, SegwitWallet, SegwitLegacyWallet, UTXOManager,
WALLET_IMPLEMENTATIONS) WALLET_IMPLEMENTATIONS, compute_tx_locktime)
from .storage import (Argon2Hash, Storage, StorageError, RetryableStorageError, from .storage import (Argon2Hash, Storage, StorageError, RetryableStorageError,
StoragePasswordError, VolatileStorage) StoragePasswordError, VolatileStorage)
from .cryptoengine import BTCEngine, BTC_P2PKH, BTC_P2SH_P2WPKH, EngineError from .cryptoengine import BTCEngine, BTC_P2PKH, BTC_P2SH_P2WPKH, EngineError

4
jmclient/jmclient/blockchaininterface.py

@ -388,6 +388,10 @@ class BitcoinCoreInterface(BlockchainInterface):
return 10000 return 10000
return int(Decimal(1e8) * Decimal(estimate)) return int(Decimal(1e8) * Decimal(estimate))
def get_current_block_height(self):
return self.rpc("getblockchaininfo", [])["blocks"]
class RegtestBitcoinCoreMixin(): class RegtestBitcoinCoreMixin():
""" """
This Mixin provides helper functions that are used in Interface classes This Mixin provides helper functions that are used in Interface classes

8
jmclient/jmclient/maker.py

@ -13,7 +13,7 @@ from binascii import unhexlify
from jmbitcoin import SerializationError, SerializationTruncationError from jmbitcoin import SerializationError, SerializationTruncationError
import jmbitcoin as btc import jmbitcoin as btc
from jmclient.wallet import estimate_tx_fee from jmclient.wallet import estimate_tx_fee, compute_tx_locktime
from jmclient.wallet_service import WalletService from jmclient.wallet_service import WalletService
from jmclient.configure import jm_single from jmclient.configure import jm_single
from jmbase.support import get_log, EXIT_SUCCESS, EXIT_FAILURE from jmbase.support import get_log, EXIT_SUCCESS, EXIT_FAILURE
@ -605,11 +605,7 @@ class P2EPMaker(Maker):
"value": new_change_amount}) "value": new_change_amount})
new_ins = [x[1] for x in utxo.values()] new_ins = [x[1] for x in utxo.values()]
new_ins.extend(my_utxos.keys()) new_ins.extend(my_utxos.keys())
# set locktime for best anonset (Core, Electrum) - most recent block. new_tx = btc.make_shuffled_tx(new_ins, new_outs, False, 2, compute_tx_locktime())
# this call should never fail so no catch here.
currentblock = jm_single().bc_interface.rpc(
"getblockchaininfo", [])["blocks"]
new_tx = btc.make_shuffled_tx(new_ins, new_outs, False, 2, currentblock)
new_tx_deser = btc.deserialize(new_tx) new_tx_deser = btc.deserialize(new_tx)
# sign our inputs before transfer # sign our inputs before transfer

8
jmclient/jmclient/taker.py

@ -16,7 +16,7 @@ from jmclient.configure import jm_single, validate_address
from jmbase.support import get_log from jmbase.support import get_log
from jmclient.support import (calc_cj_fee, weighted_order_choose, choose_orders, from jmclient.support import (calc_cj_fee, weighted_order_choose, choose_orders,
choose_sweep_orders) choose_sweep_orders)
from jmclient.wallet import estimate_tx_fee from jmclient.wallet import estimate_tx_fee, compute_tx_locktime
from jmclient.podle import generate_podle, get_podle_commitments, PoDLE from jmclient.podle import generate_podle, get_podle_commitments, PoDLE
from jmclient.wallet_service import WalletService from jmclient.wallet_service import WalletService
from .output import generate_podle_error_string from .output import generate_podle_error_string
@ -1007,14 +1007,10 @@ class P2EPTaker(Taker):
if self.my_change_addr is not None: if self.my_change_addr is not None:
self.outputs.append({'address': self.my_change_addr, self.outputs.append({'address': self.my_change_addr,
'value': my_change_value}) 'value': my_change_value})
# set locktime for best anonset (Core, Electrum) - most recent block.
# this call should never fail so no catch here.
currentblock = jm_single().bc_interface.rpc(
"getblockchaininfo", [])["blocks"]
# As for JM coinjoins, the `None` key is used for our own inputs # As for JM coinjoins, the `None` key is used for our own inputs
# to the transaction; this preparatory version contains only those. # to the transaction; this preparatory version contains only those.
tx = btc.make_shuffled_tx(self.utxos[None], self.outputs, tx = btc.make_shuffled_tx(self.utxos[None], self.outputs,
False, 2, currentblock) False, 2, compute_tx_locktime())
jlog.info('Created proposed fallback tx\n' + pprint.pformat( jlog.info('Created proposed fallback tx\n' + pprint.pformat(
btc.deserialize(tx))) btc.deserialize(tx)))
# We now sign as a courtesy, because if we disappear the recipient # We now sign as a courtesy, because if we disappear the recipient

8
jmclient/jmclient/taker_utils.py

@ -11,8 +11,9 @@ from jmbase import get_log, jmprint
from .configure import jm_single, validate_address from .configure import jm_single, validate_address
from .schedule import human_readable_schedule_entry, tweak_tumble_schedule,\ from .schedule import human_readable_schedule_entry, tweak_tumble_schedule,\
schedule_to_text schedule_to_text
from .wallet import BaseWallet, estimate_tx_fee from .wallet import BaseWallet, estimate_tx_fee, compute_tx_locktime
from jmbitcoin import deserialize, mktx, serialize, txhash, amount_to_str from jmbitcoin import deserialize, make_shuffled_tx, serialize, txhash,\
amount_to_str
from jmbase.support import EXIT_SUCCESS from jmbase.support import EXIT_SUCCESS
log = get_log() log = get_log()
@ -78,7 +79,8 @@ def direct_send(wallet_service, amount, mixdepth, destaddr, answeryes=False,
log.info("Using a fee of : " + amount_to_str(fee_est) + ".") log.info("Using a fee of : " + amount_to_str(fee_est) + ".")
if amount != 0: if amount != 0:
log.info("Using a change value of: " + amount_to_str(changeval) + ".") log.info("Using a change value of: " + amount_to_str(changeval) + ".")
txsigned = sign_tx(wallet_service, mktx(list(utxos.keys()), outs), utxos) txsigned = sign_tx(wallet_service, make_shuffled_tx(
list(utxos.keys()), outs, False, 2, compute_tx_locktime()), utxos)
log.info("Got signed transaction:\n") log.info("Got signed transaction:\n")
log.info(pformat(txsigned)) log.info(pformat(txsigned))
tx = serialize(txsigned) tx = serialize(txsigned)

11
jmclient/jmclient/wallet.py

@ -7,6 +7,7 @@ import warnings
import functools import functools
import collections import collections
import numbers import numbers
import random
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
from datetime import datetime from datetime import datetime
from copy import deepcopy from copy import deepcopy
@ -92,6 +93,16 @@ def estimate_tx_fee(ins, outs, txtype='p2pkh'):
raise NotImplementedError("Txtype: " + txtype + " not implemented.") raise NotImplementedError("Txtype: " + txtype + " not implemented.")
def compute_tx_locktime():
# set locktime for best anonset (Core, Electrum)
# most recent block or some time back in random cases
locktime = jm_single().bc_interface.get_current_block_height()
if random.randint(0, 9) == 0:
# P2EP requires locktime > 0
locktime = max(1, locktime - random.randint(0, 99))
return locktime
#FIXME: move this to a utilities file? #FIXME: move this to a utilities file?
def deprecated(func): def deprecated(func):
@functools.wraps(func) @functools.wraps(func)

Loading…
Cancel
Save