Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
pooler committed Nov 29, 2017
2 parents 411ec56 + 7173101 commit 41948b1
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 90 deletions.
2 changes: 1 addition & 1 deletion electrum-env
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
if [ -e ./env/bin/activate ]; then
source ./env/bin/activate
else
virtualenv env
virtualenv env -p `which python3`
source ./env/bin/activate
python3 setup.py install
fi
Expand Down
4 changes: 2 additions & 2 deletions gui/qt/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -2364,9 +2364,9 @@ def get_pk():
address_e.textChanged.connect(on_address)
if not d.exec_():
return

from electrum_ltc.wallet import sweep
try:
tx = self.wallet.sweep(get_pk(), self.network, self.config, get_address(), None)
tx = sweep(get_pk(), self.network, self.config, get_address(), None)
except BaseException as e:
self.show_message(str(e))
return
Expand Down
9 changes: 5 additions & 4 deletions lib/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,16 +383,17 @@ def _resolver(self, x):
raise BaseException('cannot verify alias', x)
return out['address']

@command('nw')
@command('n')
def sweep(self, privkey, destination, fee=None, nocheck=False, imax=100):
"""Sweep private keys. Returns a transaction that spends UTXOs from
privkey to a destination address. The transaction is not
broadcasted."""
from .wallet import sweep
tx_fee = satoshis(fee)
privkeys = privkey.split()
self.nocheck = nocheck
dest = self._resolver(destination)
tx = self.wallet.sweep(privkeys, self.network, self.config, dest, tx_fee, imax)
#dest = self._resolver(destination)
tx = sweep(privkeys, self.network, self.config, destination, tx_fee, imax)
return tx.as_dict() if tx else None

@command('wp')
Expand Down Expand Up @@ -707,7 +708,7 @@ def help(self):
'nocheck': (None, "Do not verify aliases"),
'imax': (None, "Maximum number of inputs"),
'fee': ("-f", "Transaction fee (in LTC)"),
'from_addr': ("-F", "Source address. If it isn't in the wallet, it will ask for the private key unless supplied in the format public_key:private_key. It's not saved in the wallet."),
'from_addr': ("-F", "Source address (must be a wallet address; use sweep to spend from non-wallet address)."),
'change_addr': ("-c", "Change address. Default is a spare address, or the source address if it's not in the wallet"),
'nbits': (None, "Number of bits of entropy"),
'entropy': (None, "Custom entropy"),
Expand Down
3 changes: 3 additions & 0 deletions lib/simple_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,9 @@ def fee_per_kb(self):
fee_rate = self.get('fee_per_kb', self.max_fee_rate()/10)
return fee_rate

def estimate_fee(self, size):
return int(self.fee_per_kb() * size / 1000.)

def update_fee_estimates(self, key, value):
self.fee_estimates[key] = value
self.fee_estimates_last_updated[key] = time.time()
Expand Down
161 changes: 79 additions & 82 deletions lib/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,81 @@
]



def relayfee(network):
RELAY_FEE = 100000
MAX_RELAY_FEE = 10 * RELAY_FEE
f = network.relay_fee if network and network.relay_fee else RELAY_FEE
return min(f, MAX_RELAY_FEE)

def dust_threshold(network):
# Change <= dust threshold is added to the tx fee
return 182 * 3 * relayfee(network) / 1000


def append_utxos_to_inputs(inputs, network, pubkey, txin_type, imax):
if txin_type != 'p2pk':
address = bitcoin.pubkey_to_address(txin_type, pubkey)
sh = bitcoin.address_to_scripthash(address)
else:
script = bitcoin.public_key_to_p2pk_script(pubkey)
sh = bitcoin.script_to_scripthash(script)
address = '(pubkey)'
u = network.synchronous_get(('blockchain.scripthash.listunspent', [sh]))
for item in u:
if len(inputs) >= imax:
break
item['address'] = address
item['type'] = txin_type
item['prevout_hash'] = item['tx_hash']
item['prevout_n'] = item['tx_pos']
item['pubkeys'] = [pubkey]
item['x_pubkeys'] = [pubkey]
item['signatures'] = [None]
item['num_sig'] = 1
inputs.append(item)

def sweep(privkeys, network, config, recipient, fee=None, imax=100):

def find_utxos_for_privkey(txin_type, privkey, compressed):
pubkey = bitcoin.public_key_from_private_key(privkey, compressed)
append_utxos_to_inputs(inputs, network, pubkey, txin_type, imax)
keypairs[pubkey] = privkey, compressed
inputs = []
keypairs = {}
for sec in privkeys:
txin_type, privkey, compressed = bitcoin.deserialize_privkey(sec)
find_utxos_for_privkey(txin_type, privkey, compressed)
# do other lookups to increase support coverage
if is_minikey(sec):
# minikeys don't have a compressed byte
# we lookup both compressed and uncompressed pubkeys
find_utxos_for_privkey(txin_type, privkey, not compressed)
elif txin_type == 'p2pkh':
# WIF serialization does not distinguish p2pkh and p2pk
# we also search for pay-to-pubkey outputs
find_utxos_for_privkey('p2pk', privkey, compressed)
if not inputs:
raise BaseException(_('No inputs found. (Note that inputs need to be confirmed)'))
total = sum(i.get('value') for i in inputs)
if fee is None:
outputs = [(TYPE_ADDRESS, recipient, total)]
tx = Transaction.from_io(inputs, outputs)
fee = config.estimate_fee(tx.estimated_size())
if total - fee < 0:
raise BaseException(_('Not enough funds on address.') + '\nTotal: %d satoshis\nFee: %d'%(total, fee))
if total - fee < dust_threshold(network):
raise BaseException(_('Not enough funds on address.') + '\nTotal: %d satoshis\nFee: %d\nDust Threshold: %d'%(total, fee, dust_threshold(network)))

outputs = [(TYPE_ADDRESS, recipient, total - fee)]
locktime = network.get_local_height()

tx = Transaction.from_io(inputs, outputs, locktime=locktime)
tx.set_rbf(True)
tx.sign(keypairs)
return tx


class Abstract_Wallet(PrintError):
"""
Wallet classes are created to handle various address generation methods.
Expand All @@ -89,7 +164,6 @@ def __init__(self, storage):
self.multiple_change = storage.get('multiple_change', False)
self.labels = storage.get('labels', {})
self.frozen_addresses = set(storage.get('frozen_addresses',[]))
self.stored_height = storage.get('stored_height', 0) # last known height (for offline mode)
self.history = storage.get('addr_history',{}) # address -> list(txid, height)

self.load_keystore()
Expand Down Expand Up @@ -316,7 +390,7 @@ def undo_verifications(self, blockchain, height):

def get_local_height(self):
""" return last known height if we are offline """
return self.network.get_local_height() if self.network else self.stored_height
return self.network.get_local_height() if self.network else self.storage.get('stored_height', 0)

def get_tx_height(self, tx_hash):
""" return the height and timestamp of a verified transaction. """
Expand Down Expand Up @@ -776,14 +850,10 @@ def get_tx_status(self, tx_hash, height, conf, timestamp):
return status, status_str

def relayfee(self):
RELAY_FEE = 100000
MAX_RELAY_FEE = 10 * RELAY_FEE
f = self.network.relay_fee if self.network and self.network.relay_fee else RELAY_FEE
return min(f, MAX_RELAY_FEE)
return relayfee(self.network)

def dust_threshold(self):
# Change <= dust threshold is added to the tx fee
return 182 * 3 * self.relayfee() / 1000
return dust_threshold(self.network)

def make_unsigned_transaction(self, inputs, outputs, config, fixed_fee=None, change_addr=None):
# check outputs
Expand Down Expand Up @@ -826,7 +896,7 @@ def make_unsigned_transaction(self, inputs, outputs, config, fixed_fee=None, cha

# Fee estimator
if fixed_fee is None:
fee_estimator = partial(self.estimate_fee, config)
fee_estimator = self.config.estimate_fee
else:
fee_estimator = lambda size: fixed_fee

Expand All @@ -853,85 +923,12 @@ def make_unsigned_transaction(self, inputs, outputs, config, fixed_fee=None, cha
run_hook('make_unsigned_transaction', self, tx)
return tx

def estimate_fee(self, config, size):
fee = int(config.fee_per_kb() * size / 1000.)
return fee

def mktx(self, outputs, password, config, fee=None, change_addr=None, domain=None):
coins = self.get_spendable_coins(domain, config)
tx = self.make_unsigned_transaction(coins, outputs, config, fee, change_addr)
self.sign_transaction(tx, password)
return tx

def _append_utxos_to_inputs(self, inputs, network, pubkey, txin_type, imax):
if txin_type != 'p2pk':
address = bitcoin.pubkey_to_address(txin_type, pubkey)
sh = bitcoin.address_to_scripthash(address)
else:
script = bitcoin.public_key_to_p2pk_script(pubkey)
sh = bitcoin.script_to_scripthash(script)
address = '(pubkey)'
u = network.synchronous_get(('blockchain.scripthash.listunspent', [sh]))
for item in u:
if len(inputs) >= imax:
break
item['address'] = address
item['type'] = txin_type
item['prevout_hash'] = item['tx_hash']
item['prevout_n'] = item['tx_pos']
item['pubkeys'] = [pubkey]
item['x_pubkeys'] = [pubkey]
item['signatures'] = [None]
item['num_sig'] = 1
inputs.append(item)

def sweep(self, privkeys, network, config, recipient, fee=None, imax=100):

def find_utxos_for_privkey(txin_type, privkey, compressed):
pubkey = bitcoin.public_key_from_private_key(privkey, compressed)
self._append_utxos_to_inputs(inputs, network, pubkey, txin_type, imax)
keypairs[pubkey] = privkey, compressed

inputs = []
keypairs = {}
for sec in privkeys:
txin_type, privkey, compressed = bitcoin.deserialize_privkey(sec)

find_utxos_for_privkey(txin_type, privkey, compressed)

# do other lookups to increase support coverage
if is_minikey(sec):
# minikeys don't have a compressed byte
# we lookup both compressed and uncompressed pubkeys
find_utxos_for_privkey(txin_type, privkey, not compressed)
elif txin_type == 'p2pkh':
# WIF serialization does not distinguish p2pkh and p2pk
# we also search for pay-to-pubkey outputs
find_utxos_for_privkey('p2pk', privkey, compressed)

if not inputs:
raise BaseException(_('No inputs found. (Note that inputs need to be confirmed)'))

total = sum(i.get('value') for i in inputs)
if fee is None:
outputs = [(TYPE_ADDRESS, recipient, total)]
tx = Transaction.from_io(inputs, outputs)
fee = self.estimate_fee(config, tx.estimated_size())

if total - fee < 0:
raise BaseException(_('Not enough funds on address.') + '\nTotal: %d satoshis\nFee: %d'%(total, fee))

if total - fee < self.dust_threshold():
raise BaseException(_('Not enough funds on address.') + '\nTotal: %d satoshis\nFee: %d\nDust Threshold: %d'%(total, fee, self.dust_threshold()))

outputs = [(TYPE_ADDRESS, recipient, total - fee)]
locktime = self.get_local_height()

tx = Transaction.from_io(inputs, outputs, locktime=locktime)
tx.set_rbf(True)
tx.sign(keypairs)
return tx

def is_frozen(self, addr):
return addr in self.frozen_addresses

Expand Down
4 changes: 3 additions & 1 deletion scripts/get_history
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import sys
from electrum_ltc import Network
from electrum_ltc.util import json_encode, print_msg
from electrum_ltc import bitcoin

try:
addr = sys.argv[1]
Expand All @@ -12,5 +13,6 @@ except Exception:

n = Network()
n.start()
h = n.synchronous_get(('blockchain.address.get_history',[addr]))
_hash = bitcoin.address_to_scripthash(addr)
h = n.synchronous_get(('blockchain.scripthash.get_history',[_hash]))
print_msg(json_encode(h))

0 comments on commit 41948b1

Please sign in to comment.