Merge branch 'master' of github.com:spesmilo/electrum
This commit is contained in:
commit
36bc0184f4
|
@ -1,3 +1,15 @@
|
|||
|
||||
# Release 1.7.2:
|
||||
|
||||
* Transactions that are in the same block are displayed in chronological order in the history.
|
||||
* The client computes transaction priority and rejects zero-fee transactions that need a fee.
|
||||
* The default fee was lowered to 200 uBTC per kb.
|
||||
* Due to an internal format change, your history may be pruned when
|
||||
you open your wallet for the first time after upgrading to 1.7.2. If
|
||||
this is the case, please visit a full server to restore your full
|
||||
history. You will only need to do that once.
|
||||
|
||||
|
||||
# Release 1.7.1: bugfixes.
|
||||
|
||||
|
||||
|
|
5
app.fil
5
app.fil
|
@ -2,3 +2,8 @@ gui/gui_gtk.py
|
|||
gui/gui_classic.py
|
||||
gui/gui_lite.py
|
||||
gui/history_widget.py
|
||||
plugins/aliases.py
|
||||
plugins/pointofsale.py
|
||||
plugins/labels.py
|
||||
plugins/qrscanner.py
|
||||
plugins/virtualkeyboard.py
|
||||
|
|
2
electrum
2
electrum
|
@ -72,7 +72,7 @@ def arg_parser():
|
|||
parser.add_option("-a", "--all", action="store_true", dest="show_all", default=False, help="show all addresses")
|
||||
parser.add_option("-b", "--balance", action="store_true", dest="show_balance", default=False, help="show the balance of listed addresses")
|
||||
parser.add_option("-l", "--labels", action="store_true", dest="show_labels", default=False, help="show the labels of listed addresses")
|
||||
parser.add_option("-f", "--fee", dest="tx_fee", default="0.005", help="set tx fee")
|
||||
parser.add_option("-f", "--fee", dest="tx_fee", default=None, help="set tx fee")
|
||||
parser.add_option("-F", "--fromaddr", dest="from_addr", default=None, help="set source address for payto/mktx. 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.")
|
||||
parser.add_option("-c", "--changeaddr", dest="change_addr", default=None, help="set the change address for payto/mktx. default is a spare address, or the source address if it's not in the wallet")
|
||||
parser.add_option("-s", "--server", dest="server", default=None, help="set server host:port:protocol, where protocol is t or h")
|
||||
|
|
|
@ -32,6 +32,7 @@ from PyQt4.QtCore import *
|
|||
import PyQt4.QtCore as QtCore
|
||||
import PyQt4.QtGui as QtGui
|
||||
from electrum.interface import DEFAULT_SERVERS
|
||||
from electrum.bitcoin import MIN_RELAY_TX_FEE
|
||||
|
||||
try:
|
||||
import icons_rc
|
||||
|
@ -795,6 +796,10 @@ class ElectrumWindow(QMainWindow):
|
|||
self.show_message(str(e))
|
||||
return
|
||||
|
||||
if tx.requires_fee(self.wallet.verifier) and fee < MIN_RELAY_TX_FEE:
|
||||
QMessageBox.warning(self, _('Error'), _("This transaction requires a higher fee, or it will not be propagated by the network."), _('OK'))
|
||||
return
|
||||
|
||||
self.run_hook('send_tx', tx)
|
||||
|
||||
if label:
|
||||
|
@ -1928,11 +1933,11 @@ class ElectrumWindow(QMainWindow):
|
|||
fee_e = QLineEdit()
|
||||
fee_e.setText("%s"% str( Decimal( self.wallet.fee)/100000000 ) )
|
||||
grid_wallet.addWidget(fee_e, 0, 2)
|
||||
msg = _('Fee per transaction input. Transactions involving multiple inputs tend to require a higher fee.') + ' ' \
|
||||
+ _('Recommended value') + ': 0.001'
|
||||
msg = _('Fee per kilobyte of transaction.') + ' ' \
|
||||
+ _('Recommended value') + ': 0.0001'
|
||||
grid_wallet.addWidget(HelpButton(msg), 0, 3)
|
||||
fee_e.textChanged.connect(lambda: numbify(fee_e,False))
|
||||
if not self.config.is_modifiable('fee'):
|
||||
if not self.config.is_modifiable('fee_per_kb'):
|
||||
for w in [fee_e, fee_label]: w.setEnabled(False)
|
||||
|
||||
usechange_label = QLabel(_('Use change addresses'))
|
||||
|
|
|
@ -35,6 +35,7 @@ MONOSPACE_FONT = 'Lucida Console' if platform.system() == 'Windows' else 'monosp
|
|||
|
||||
from electrum.util import format_satoshis
|
||||
from electrum.interface import DEFAULT_SERVERS
|
||||
from electrum.bitcoin import MIN_RELAY_TX_FEE
|
||||
|
||||
def numbify(entry, is_int = False):
|
||||
text = entry.get_text().strip()
|
||||
|
@ -198,7 +199,7 @@ def run_settings_dialog(wallet, parent):
|
|||
fee_entry.connect('changed', numbify, False)
|
||||
fee_entry.show()
|
||||
fee.pack_start(fee_entry,False,False, 10)
|
||||
add_help_button(fee, 'Fee per transaction input. Transactions involving multiple inputs tend to have a higher fee. Recommended value:0.0005')
|
||||
add_help_button(fee, 'Fee per kilobyte of transaction. Recommended value:0.0001')
|
||||
fee.show()
|
||||
vbox.pack_start(fee, False,False, 5)
|
||||
|
||||
|
@ -843,6 +844,11 @@ class ElectrumWindow:
|
|||
except BaseException, e:
|
||||
self.show_message(str(e))
|
||||
return
|
||||
|
||||
if tx.requires_fee(self.wallet.verifier) and fee < MIN_RELAY_TX_FEE:
|
||||
self.show_message( "This transaction requires a higher fee, or it will not be propagated by the network." )
|
||||
return
|
||||
|
||||
|
||||
if label:
|
||||
self.wallet.labels[tx.hash()] = label
|
||||
|
|
|
@ -318,14 +318,14 @@ class ElectrumGui:
|
|||
def settings_dialog(self):
|
||||
out = self.run_dialog('Settings', [
|
||||
{'label':'Default GUI', 'type':'list', 'choices':['classic','lite','gtk','text'], 'value':self.config.get('gui')},
|
||||
{'label':'Default fee', 'type':'satoshis', 'value': format_satoshis(self.config.get('fee')).strip() }
|
||||
{'label':'Default fee', 'type':'satoshis', 'value': format_satoshis(self.config.get('fee_per_kb')).strip() }
|
||||
], buttons = 1)
|
||||
if out:
|
||||
if out.get('Default GUI'):
|
||||
self.config.set_key('gui', out['Default GUI'], True)
|
||||
if out.get('Default fee'):
|
||||
fee = int ( Decimal( out['Default fee']) *10000000 )
|
||||
self.config.set_key('fee', fee, True)
|
||||
self.config.set_key('fee_per_kb', fee, True)
|
||||
|
||||
|
||||
def password_dialog(self):
|
||||
|
|
|
@ -577,6 +577,7 @@ class BIP32Sequence:
|
|||
|
||||
################################## transactions
|
||||
|
||||
MIN_RELAY_TX_FEE = 10000
|
||||
|
||||
class Transaction:
|
||||
|
||||
|
@ -876,6 +877,26 @@ class Transaction:
|
|||
return out
|
||||
|
||||
|
||||
def requires_fee(self, verifier):
|
||||
# see https://en.bitcoin.it/wiki/Transaction_fees
|
||||
threshold = 57600000
|
||||
size = len(self.raw)/2
|
||||
if size >= 10000:
|
||||
return True
|
||||
|
||||
for o in self.outputs:
|
||||
value = o[1]
|
||||
if value < 1000000:
|
||||
return True
|
||||
sum = 0
|
||||
for i in self.inputs:
|
||||
age = verifier.get_confirmations(i["tx_hash"])[0]
|
||||
sum += i["value"] * age
|
||||
priority = sum / size
|
||||
print_error(priority, threshold)
|
||||
return priority < threshold
|
||||
|
||||
|
||||
|
||||
|
||||
def test_bip32():
|
||||
|
|
|
@ -323,7 +323,12 @@ def match_decoded(decoded, to_match):
|
|||
return True
|
||||
|
||||
def get_address_from_input_script(bytes):
|
||||
decoded = [ x for x in script_GetOp(bytes) ]
|
||||
try:
|
||||
decoded = [ x for x in script_GetOp(bytes) ]
|
||||
except:
|
||||
# coinbase transactions raise an exception
|
||||
print_error("cannot find address in input script", bytes.encode('hex'))
|
||||
return [], [], "(None)"
|
||||
|
||||
# non-generated TxIn transactions push a signature
|
||||
# (seventy-something bytes) and then their public key
|
||||
|
|
|
@ -35,7 +35,7 @@ class WalletVerifier(threading.Thread):
|
|||
self.transactions = {} # requested verifications (with height sent by the requestor)
|
||||
self.interface.register_channel('verifier')
|
||||
|
||||
self.verified_tx = config.get('verified_tx2',{}) # height, timestamp of verified transactions
|
||||
self.verified_tx = config.get('verified_tx3',{}) # height, timestamp of verified transactions
|
||||
self.merkle_roots = config.get('merkle_roots',{}) # hashed by me
|
||||
|
||||
self.targets = config.get('targets',{}) # compute targets
|
||||
|
@ -50,7 +50,7 @@ class WalletVerifier(threading.Thread):
|
|||
""" return the number of confirmations of a monitored transaction. """
|
||||
with self.lock:
|
||||
if tx in self.verified_tx:
|
||||
height, timestamp = self.verified_tx[tx]
|
||||
height, timestamp, pos = self.verified_tx[tx]
|
||||
conf = (self.local_height - height + 1)
|
||||
else:
|
||||
conf = 0
|
||||
|
@ -183,7 +183,8 @@ class WalletVerifier(threading.Thread):
|
|||
|
||||
def verify_merkle(self, tx_hash, result):
|
||||
tx_height = result.get('block_height')
|
||||
self.merkle_roots[tx_hash] = self.hash_merkle_root(result['merkle'], tx_hash, result.get('pos'))
|
||||
pos = result.get('pos')
|
||||
self.merkle_roots[tx_hash] = self.hash_merkle_root(result['merkle'], tx_hash, pos)
|
||||
header = self.read_header(tx_height)
|
||||
if not header: return
|
||||
assert header.get('merkle_root') == self.merkle_roots[tx_hash]
|
||||
|
@ -191,9 +192,9 @@ class WalletVerifier(threading.Thread):
|
|||
header = self.read_header(tx_height)
|
||||
timestamp = header.get('timestamp')
|
||||
with self.lock:
|
||||
self.verified_tx[tx_hash] = (tx_height, timestamp)
|
||||
self.verified_tx[tx_hash] = (tx_height, timestamp, pos)
|
||||
print_error("verified %s"%tx_hash)
|
||||
self.config.set_key('verified_tx2', self.verified_tx, True)
|
||||
self.config.set_key('verified_tx3', self.verified_tx, True)
|
||||
self.interface.trigger_callback('updated')
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
ELECTRUM_VERSION = "1.7.1" # version of the client package
|
||||
ELECTRUM_VERSION = "1.7.2" # version of the client package
|
||||
PROTOCOL_VERSION = '0.6' # protocol version requested
|
||||
SEED_VERSION = 4 # bump this every time the seed generation is modified
|
||||
TRANSLATION_ID = 3992 # version of the wiki page
|
||||
TRANSLATION_ID = 4012 # version of the wiki page
|
||||
|
|
|
@ -74,7 +74,7 @@ class Wallet:
|
|||
self.seed_version = config.get('seed_version', SEED_VERSION)
|
||||
self.gap_limit = config.get('gap_limit', 5)
|
||||
self.use_change = config.get('use_change',True)
|
||||
self.fee = int(config.get('fee',100000))
|
||||
self.fee = int(config.get('fee_per_kb',20000))
|
||||
self.num_zeros = int(config.get('num_zeros',0))
|
||||
self.use_encryption = config.get('use_encryption', False)
|
||||
self.seed = config.get('seed', '') # encrypted
|
||||
|
@ -103,7 +103,6 @@ class Wallet:
|
|||
# not saved
|
||||
self.prevout_values = {} # my own transaction outputs
|
||||
self.spent_outputs = []
|
||||
self.receipt = None # next receipt
|
||||
|
||||
# spv
|
||||
self.verifier = None
|
||||
|
@ -114,6 +113,7 @@ class Wallet:
|
|||
|
||||
self.up_to_date = False
|
||||
self.lock = threading.Lock()
|
||||
self.transaction_lock = threading.Lock()
|
||||
self.tx_event = threading.Event()
|
||||
|
||||
if self.seed_version != SEED_VERSION:
|
||||
|
@ -429,13 +429,10 @@ class Wallet:
|
|||
|
||||
def update_tx_outputs(self, tx_hash):
|
||||
tx = self.transactions.get(tx_hash)
|
||||
i = 0
|
||||
for item in tx.outputs:
|
||||
addr, value = item
|
||||
|
||||
for i, (addr, value) in enumerate(tx.outputs):
|
||||
key = tx_hash+ ':%d'%i
|
||||
with self.lock:
|
||||
self.prevout_values[key] = value
|
||||
i += 1
|
||||
self.prevout_values[key] = value
|
||||
|
||||
for item in tx.inputs:
|
||||
if self.is_mine(item.get('address')):
|
||||
|
@ -453,13 +450,11 @@ class Wallet:
|
|||
for tx_hash, tx_height in h:
|
||||
tx = self.transactions.get(tx_hash)
|
||||
if not tx: continue
|
||||
i = 0
|
||||
for item in tx.outputs:
|
||||
addr, value = item
|
||||
|
||||
for i, (addr, value) in enumerate(tx.outputs):
|
||||
if addr == address:
|
||||
key = tx_hash + ':%d'%i
|
||||
received_coins.append(key)
|
||||
i +=1
|
||||
|
||||
for tx_hash, tx_height in h:
|
||||
tx = self.transactions.get(tx_hash)
|
||||
|
@ -474,13 +469,10 @@ class Wallet:
|
|||
if key in received_coins:
|
||||
v -= value
|
||||
|
||||
i = 0
|
||||
for item in tx.outputs:
|
||||
addr, value = item
|
||||
for i, (addr, value) in enumerate(tx.outputs):
|
||||
key = tx_hash + ':%d'%i
|
||||
if addr == address:
|
||||
v += value
|
||||
i += 1
|
||||
|
||||
if tx_height:
|
||||
c += v
|
||||
|
@ -565,14 +557,20 @@ class Wallet:
|
|||
total += v
|
||||
|
||||
inputs.append( item )
|
||||
fee = self.fee*len(inputs) if fixed_fee is None else fixed_fee
|
||||
if fixed_fee is None:
|
||||
estimated_size = len(inputs) * 180 + 80 # this assumes non-compressed keys
|
||||
fee = self.fee * int(round(estimated_size/1024.))
|
||||
if fee == 0: fee = self.fee
|
||||
else:
|
||||
fee = fixed_fee
|
||||
if total >= amount + fee: break
|
||||
else:
|
||||
#print "not enough funds: %s %s"%(format_satoshis(total), format_satoshis(fee))
|
||||
inputs = []
|
||||
|
||||
return inputs, total, fee
|
||||
|
||||
|
||||
|
||||
def add_tx_change( self, outputs, amount, fee, total, change_addr=None ):
|
||||
change_amount = total - ( amount + fee )
|
||||
if change_amount != 0:
|
||||
|
@ -602,19 +600,17 @@ class Wallet:
|
|||
|
||||
def receive_tx_callback(self, tx_hash, tx, tx_height):
|
||||
|
||||
|
||||
if not self.check_new_tx(tx_hash, tx):
|
||||
# may happen due to pruning
|
||||
print_error("received transaction that is no longer referenced in history", tx_hash)
|
||||
return
|
||||
|
||||
with self.lock:
|
||||
with self.transaction_lock:
|
||||
self.transactions[tx_hash] = tx
|
||||
|
||||
#tx_height = tx.get('height')
|
||||
if self.verifier and tx_height>0:
|
||||
self.verifier.add(tx_hash, tx_height)
|
||||
|
||||
self.update_tx_outputs(tx_hash)
|
||||
if self.verifier and tx_height>0:
|
||||
self.verifier.add(tx_hash, tx_height)
|
||||
self.update_tx_outputs(tx_hash)
|
||||
|
||||
self.save()
|
||||
|
||||
|
@ -636,29 +632,29 @@ class Wallet:
|
|||
|
||||
|
||||
def get_tx_history(self):
|
||||
with self.lock:
|
||||
with self.transaction_lock:
|
||||
history = self.transactions.items()
|
||||
history.sort(key = lambda x: self.verifier.get_height(x[0]) if self.verifier.get_height(x[0]) else 1e12)
|
||||
result = []
|
||||
history.sort(key = lambda x: self.verifier.verified_tx.get(x[0]) if self.verifier.verified_tx.get(x[0]) else (1e12,0,0))
|
||||
result = []
|
||||
|
||||
balance = 0
|
||||
for tx_hash, tx in history:
|
||||
is_mine, v, fee = self.get_tx_value(tx)
|
||||
if v is not None: balance += v
|
||||
c, u = self.get_balance()
|
||||
balance = 0
|
||||
for tx_hash, tx in history:
|
||||
is_mine, v, fee = self.get_tx_value(tx)
|
||||
if v is not None: balance += v
|
||||
c, u = self.get_balance()
|
||||
|
||||
if balance != c+u:
|
||||
v_str = format_satoshis( c+u - balance, True, self.num_zeros)
|
||||
result.append( ('', 1000, 0, c+u-balance, None, c+u-balance, None ) )
|
||||
if balance != c+u:
|
||||
#v_str = format_satoshis( c+u - balance, True, self.num_zeros)
|
||||
result.append( ('', 1000, 0, c+u-balance, None, c+u-balance, None ) )
|
||||
|
||||
balance = c + u - balance
|
||||
for tx_hash, tx in history:
|
||||
conf, timestamp = self.verifier.get_confirmations(tx_hash) if self.verifier else (None, None)
|
||||
is_mine, value, fee = self.get_tx_value(tx)
|
||||
if value is not None:
|
||||
balance += value
|
||||
balance = c + u - balance
|
||||
for tx_hash, tx in history:
|
||||
conf, timestamp = self.verifier.get_confirmations(tx_hash) if self.verifier else (None, None)
|
||||
is_mine, value, fee = self.get_tx_value(tx)
|
||||
if value is not None:
|
||||
balance += value
|
||||
|
||||
result.append( (tx_hash, conf, is_mine, value, fee, balance, timestamp) )
|
||||
result.append( (tx_hash, conf, is_mine, value, fee, balance, timestamp) )
|
||||
|
||||
return result
|
||||
|
||||
|
@ -768,9 +764,6 @@ class Wallet:
|
|||
out = self.tx_result
|
||||
if out != tx_hash:
|
||||
return False, "error: " + out
|
||||
if self.receipt:
|
||||
self.receipts[tx_hash] = self.receipt
|
||||
self.receipt = None
|
||||
return True, out
|
||||
|
||||
|
||||
|
@ -831,7 +824,7 @@ class Wallet:
|
|||
s = {
|
||||
'use_encryption': self.use_encryption,
|
||||
'use_change': self.use_change,
|
||||
'fee': self.fee,
|
||||
'fee_per_kb': self.fee,
|
||||
'accounts': self.accounts,
|
||||
'addr_history': self.history,
|
||||
'labels': self.labels,
|
||||
|
@ -859,6 +852,12 @@ class Wallet:
|
|||
self.verifier.add(tx_hash, tx_height)
|
||||
|
||||
|
||||
# if we are on a pruning server, remove unverified transactions
|
||||
vr = self.verifier.transactions.keys() + self.verifier.verified_tx.keys()
|
||||
for tx_hash in self.transactions.keys():
|
||||
if tx_hash not in vr:
|
||||
self.transactions.pop(tx_hash)
|
||||
|
||||
|
||||
|
||||
def check_new_history(self, addr, hist):
|
||||
|
@ -903,6 +902,7 @@ class Wallet:
|
|||
ext_requests.append( ('blockchain.address.get_history', [_addr]) )
|
||||
|
||||
ext_h = self.interface.synchronous_get(ext_requests)
|
||||
print_error("sync:", ext_requests, ext_h)
|
||||
height = None
|
||||
for h in ext_h:
|
||||
if h == ['*']: continue
|
||||
|
|
|
@ -25,7 +25,8 @@ if __name__ == '__main__':
|
|||
shutil.copytree("aes",'dist/e4a-%s/aes'%version)
|
||||
shutil.copytree("lib",'dist/e4a-%s/lib'%version)
|
||||
os.mkdir('dist/e4a-%s/gui'%version)
|
||||
shutil.copy("gui/gui_android.py",'dist/e4a-%s/gui'%version)
|
||||
for n in ['gui_android.py', 'pyqrnative.py', 'bmp.py']:
|
||||
shutil.copy("gui/%s"%n,'dist/e4a-%s/gui'%version)
|
||||
open('dist/e4a-%s/gui/__init__.py'%version,'w').close()
|
||||
|
||||
os.chdir("dist")
|
||||
|
|
Loading…
Reference in New Issue