Browse Source

Merge branch 'master' of github.com:spesmilo/electrum

283
slush 12 years ago
parent
commit
36bc0184f4
  1. 12
      RELEASE-NOTES
  2. 5
      app.fil
  3. 2
      electrum
  4. 11
      gui/gui_classic.py
  5. 8
      gui/gui_gtk.py
  6. 4
      gui/gui_text.py
  7. 21
      lib/bitcoin.py
  8. 7
      lib/deserialize.py
  9. 11
      lib/verifier.py
  10. 4
      lib/version.py
  11. 94
      lib/wallet.py
  12. 3
      make_packages

12
RELEASE-NOTES

@ -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

@ -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

@ -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")

11
gui/gui_classic.py

@ -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'))

8
gui/gui_gtk.py

@ -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

4
gui/gui_text.py

@ -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):

21
lib/bitcoin.py

@ -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():

7
lib/deserialize.py

@ -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

11
lib/verifier.py

@ -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')

4
lib/version.py

@ -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

94
lib/wallet.py

@ -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

3
make_packages

@ -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…
Cancel
Save