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

This commit is contained in:
slush 2013-04-01 04:24:26 +02:00
commit 36bc0184f4
12 changed files with 119 additions and 63 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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