Merge branch 'master' of gitorious.org:electrum/electrum

This commit is contained in:
ThomasV 2012-04-16 18:38:27 +04:00
commit e42cd36318
8 changed files with 1021 additions and 375 deletions

33
client/ANDROID Normal file
View File

@ -0,0 +1,33 @@
INSTALLATION INSTRUCTIONS FOR ANDROID
1. Install sl4a and py4a : you will need at least revision r5x12 of sl4a
To install these APKs, just visit the links below with your phone and
click on the apk link, or scan the qr code
sl4a: http://code.google.com/p/android-scripting/wiki/Unofficial
py4a: http://code.google.com/p/python-for-android/downloads/detail?name=PythonForAndroid_r5.apk
2. copy the following files in /sdcard/sl4a/scripts:
bmp.py
electrum4a.py
interface.py
mnemonic.py
msqr.py
pyqrnative.py
ripemd.py
version.py
wallet.py
aes (directory)
ecdsa (directory)
Note: The aes and ecdsa directories are not included in the git
repository. You will have to find them on your system, or you can
find them in the distributed version, Electrum tar.gz or Electrum.zip
3. to run the application, open sl4a and click on electrum4a.py

206
client/bmp.py Normal file
View File

@ -0,0 +1,206 @@
# -*- coding: utf-8 -*-
"""
bmp.py - module for constructing simple BMP graphics files
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
__version__ = "0.3"
__about = "bmp module, version %s, written by Paul McGuire, October, 2003, updated by Margus Laak, September, 2009" % __version__
from math import ceil, hypot
def shortToString(i):
hi = (i & 0xff00) >> 8
lo = i & 0x00ff
return chr(lo) + chr(hi)
def longToString(i):
hi = (long(i) & 0x7fff0000) >> 16
lo = long(i) & 0x0000ffff
return shortToString(lo) + shortToString(hi)
def long24ToString(i):
return chr(i & 0xff) + chr(i >> 8 & 0xff) + chr(i >> 16 & 0xff)
def stringToLong(input_string, offset):
return ord(input_string[offset+3]) << 24 | ord(input_string[offset+2]) << 16 | ord(input_string[offset+1]) << 8 | ord(input_string[offset])
def stringToLong24(input_string, offset):
return ord(input_string[offset+2]) << 16 | ord(input_string[offset+1]) << 8 | ord(input_string[offset])
class Color(object):
"""class for specifying colors while drawing BitMap elements"""
__slots__ = [ 'red', 'grn', 'blu' ]
__shade = 32
def __init__( self, r=0, g=0, b=0 ):
self.red = r
self.grn = g
self.blu = b
def __setattr__(self, name, value):
if hasattr(self, name):
raise AttributeError, "Color is immutable"
else:
object.__setattr__(self, name, value)
def __str__( self ):
return "R:%d G:%d B:%d" % (self.red, self.grn, self.blu )
def __hash__( self ):
return ( ( long(self.blu) ) +
( long(self.grn) << 8 ) +
( long(self.red) << 16 ) )
def __eq__( self, other ):
return (self is other) or (self.toLong == other.toLong)
def lighten( self ):
return Color(
min( self.red + Color.__shade, 255),
min( self.grn + Color.__shade, 255),
min( self.blu + Color.__shade, 255)
)
def darken( self ):
return Color(
max( self.red - Color.__shade, 0),
max( self.grn - Color.__shade, 0),
max( self.blu - Color.__shade, 0)
)
def toLong( self ):
return self.__hash__()
def fromLong( l ):
b = l & 0xff
l = l >> 8
g = l & 0xff
l = l >> 8
r = l & 0xff
return Color( r, g, b )
fromLong = staticmethod(fromLong)
# define class constants for common colors
Color.BLACK = Color( 0, 0, 0 )
Color.RED = Color( 255, 0, 0 )
Color.GREEN = Color( 0, 255, 0 )
Color.BLUE = Color( 0, 0, 255 )
Color.CYAN = Color( 0, 255, 255 )
Color.MAGENTA = Color( 255, 0, 255 )
Color.YELLOW = Color( 255, 255, 0 )
Color.WHITE = Color( 255, 255, 255 )
Color.DKRED = Color( 128, 0, 0 )
Color.DKGREEN = Color( 0, 128, 0 )
Color.DKBLUE = Color( 0, 0, 128 )
Color.TEAL = Color( 0, 128, 128 )
Color.PURPLE = Color( 128, 0, 128 )
Color.BROWN = Color( 128, 128, 0 )
Color.GRAY = Color( 128, 128, 128 )
class BitMap(object):
"""class for drawing and saving simple Windows bitmap files"""
LINE_SOLID = 0
LINE_DASHED = 1
LINE_DOTTED = 2
LINE_DOT_DASH=3
_DASH_LEN = 12.0
_DOT_LEN = 6.0
_DOT_DASH_LEN = _DOT_LEN + _DASH_LEN
def __init__( self, width, height,
bkgd = Color.WHITE, frgd = Color.BLACK ):
self.wd = int( ceil(width) )
self.ht = int( ceil(height) )
self.bgcolor = 0
self.fgcolor = 1
self.palette = []
self.palette.append( bkgd.toLong() )
self.palette.append( frgd.toLong() )
self.currentPen = self.fgcolor
tmparray = [ self.bgcolor ] * self.wd
self.bitarray = [ tmparray[:] for i in range( self.ht ) ]
self.currentPen = 1
def plotPoint( self, x, y ):
if ( 0 <= x < self.wd and 0 <= y < self.ht ):
x = int(x)
y = int(y)
self.bitarray[y][x] = self.currentPen
def _saveBitMapNoCompression( self ):
line_padding = (4 - (self.wd % 4)) % 4
# write bitmap header
_bitmap = "BM"
_bitmap += longToString( 54 + self.ht*(self.wd*3 + line_padding) ) # DWORD size in bytes of the file
_bitmap += longToString( 0 ) # DWORD 0
_bitmap += longToString( 54 )
_bitmap += longToString( 40 ) # DWORD header size = 40
_bitmap += longToString( self.wd ) # DWORD image width
_bitmap += longToString( self.ht ) # DWORD image height
_bitmap += shortToString( 1 ) # WORD planes = 1
_bitmap += shortToString( 24 ) # WORD bits per pixel = 8
_bitmap += longToString( 0 ) # DWORD compression = 0
_bitmap += longToString( self.ht * (self.wd * 3 + line_padding) ) # DWORD sizeimage = size in bytes of the bitmap = width * height
_bitmap += longToString( 0 ) # DWORD horiz pixels per meter (?)
_bitmap += longToString( 0 ) # DWORD ver pixels per meter (?)
_bitmap += longToString( 0 ) # DWORD number of colors used = 256
_bitmap += longToString( 0 ) # DWORD number of "import colors = len( self.palette )
# write pixels
self.bitarray.reverse()
for row in self.bitarray:
for pixel in row:
c = self.palette[pixel]
_bitmap += long24ToString(c)
for i in range(line_padding):
_bitmap += chr( 0 )
return _bitmap
def saveFile( self, filename):
_b = self._saveBitMapNoCompression( )
f = file(filename, 'wb')
f.write(_b)
f.close()
if __name__ == "__main__":
bmp = BitMap( 10, 10 )
bmp.plotPoint( 5, 5 )
bmp.plotPoint( 0, 0 )
bmp.saveFile( "test.bmp" )

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -1111,7 +1111,7 @@ class ElectrumWindow:
self.network_button.set_tooltip_text("Connected to %s:%d.\n%d blocks\nresponse time: %f"%(interface.host, interface.port, self.wallet.blocks, interface.rtime))
c, u = self.wallet.get_balance()
text = "Balance: %s "%( format_satoshis(c) )
if u: text += "[%s unconfirmed]"%( format_satoshis(u,True) )
if u: text += "[%s unconfirmed]"%( format_satoshis(u,True).strip() )
else:
self.status_image.set_from_stock(gtk.STOCK_NO, gtk.ICON_SIZE_MENU)
self.network_button.set_tooltip_text("Trying to contact %s.\n%d blocks"%(interface.host, self.wallet.blocks))

View File

@ -100,8 +100,6 @@ class QRCodeWidget(QWidget):
super(QRCodeWidget, self).__init__()
self.addr = addr
self.setGeometry(300, 300, 350, 350)
self.setWindowTitle('Colors')
self.show()
self.qr = pyqrnative.QRCode(4, pyqrnative.QRErrorCorrectLevel.H)
self.qr.addData(addr)
self.qr.make()
@ -144,6 +142,8 @@ class ElectrumWindow(QMainWindow):
def __init__(self, wallet):
QMainWindow.__init__(self)
self.wallet = wallet
self.wallet.gui_callback = self.update_callback
self.funds_error = False
self.tabs = tabs = QTabWidget(self)
@ -164,11 +164,11 @@ class ElectrumWindow(QMainWindow):
QShortcut(QKeySequence("Ctrl+Q"), self, self.close)
QShortcut(QKeySequence("Ctrl+PgUp"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() - 1 )%tabs.count() ))
QShortcut(QKeySequence("Ctrl+PgDown"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() + 1 )%tabs.count() ))
self.connect(self, QtCore.SIGNAL('updatesignal'), self.update_wallet)
def connect_slots(self, sender):
self.connect(sender, QtCore.SIGNAL('timersignal'), self.update_wallet)
self.connect(sender, QtCore.SIGNAL('timersignal'), self.check_recipient)
self.previous_payto_e=''
@ -189,6 +189,9 @@ class ElectrumWindow(QMainWindow):
self.payto_e.setText(s)
def update_callback(self):
self.emit(QtCore.SIGNAL('updatesignal'))
def update_wallet(self):
if self.wallet.interface.is_connected:
if self.wallet.blocks == -1:
@ -203,7 +206,7 @@ class ElectrumWindow(QMainWindow):
else:
c, u = self.wallet.get_balance()
text = "Balance: %s "%( format_satoshis(c) )
if u: text += "[%s unconfirmed]"%( format_satoshis(u,True) )
if u: text += "[%s unconfirmed]"%( format_satoshis(u,True).strip() )
icon = QIcon(":icons/status_connected.png")
else:
text = "Not connected"
@ -215,8 +218,7 @@ class ElectrumWindow(QMainWindow):
self.statusBar().showMessage(text)
self.status_button.setIcon( icon )
if self.wallet.was_updated and self.wallet.up_to_date:
self.wallet.was_updated = False
if self.wallet.up_to_date:
self.textbox.setText( self.wallet.banner )
self.update_history_tab()
self.update_receive_tab()
@ -522,17 +524,7 @@ class ElectrumWindow(QMainWindow):
addr = unicode( i.text(0) )
return addr
def showqrcode(address):
if not address: return
d = QDialog(self)
d.setModal(1)
d.setMinimumSize(270, 300)
vbox = QVBoxLayout()
vbox.addWidget(QRCodeWidget(address))
vbox.addLayout(ok_cancel_buttons(d))
d.setLayout(vbox)
d.exec_()
qrButton = EnterButton("QR",lambda: showqrcode(get_addr(l)))
qrButton = EnterButton("QR",lambda: ElectrumWindow.showqrcode(get_addr(l)))
def copy2clipboard(addr):
self.app.clipboard().setText(addr)
@ -665,19 +657,20 @@ class ElectrumWindow(QMainWindow):
+ ' '.join(mnemonic.mn_encode(seed)) + "\""
QMessageBox.information(parent, 'Seed', msg, 'OK')
ElectrumWindow.showqrcode(seed)
def showqrcode(address):
if not address: return
d = QDialog(None)
d.setModal(1)
d.setMinimumSize(270, 300)
vbox = QVBoxLayout()
vbox.addWidget(QRCodeWidget(address))
vbox.addLayout(ok_cancel_buttons(d))
d.setLayout(vbox)
d.exec_()
showqrcode(seed)
@staticmethod
def showqrcode(address):
if not address: return
d = QDialog(None)
d.setModal(1)
d.setWindowTitle(address)
d.setMinimumSize(270, 300)
vbox = QVBoxLayout()
vbox.addWidget(QRCodeWidget(address))
vbox.addLayout(ok_cancel_buttons(d))
d.setLayout(vbox)
d.exec_()
def question(self, msg):
return QMessageBox.question(self, 'Message', msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes
@ -1059,4 +1052,6 @@ class ElectrumGui():
if url: w.set_url(url)
w.app = self.app
w.connect_slots(s)
w.update_wallet()
self.app.exec_()

View File

@ -316,6 +316,7 @@ class TcpStratumInterface(Interface):
traceback.print_exc(file=sys.stdout)
self.is_connected = False
print "poking"
self.poke()
def send(self, messages):
@ -448,27 +449,33 @@ class WalletSynchronizer(threading.Thread):
def run(self):
import socket, time
while True:
try:
while self.interface.is_connected:
new_addresses = self.wallet.synchronize()
if new_addresses:
self.interface.subscribe(new_addresses)
for addr in new_addresses:
with self.wallet.lock:
self.wallet.addresses_waiting_for_status.append(addr)
while self.interface.is_connected:
new_addresses = self.wallet.synchronize()
if new_addresses:
self.interface.subscribe(new_addresses)
for addr in new_addresses:
with self.wallet.lock:
self.wallet.addresses_waiting_for_status.append(addr)
if self.wallet.is_up_to_date():
if self.wallet.is_up_to_date():
if not self.wallet.up_to_date:
self.wallet.up_to_date = True
self.wallet.was_updated = True
self.wallet.up_to_date_event.set()
else:
else:
if self.wallet.up_to_date:
self.wallet.up_to_date = False
self.wallet.was_updated = True
response = self.interface.responses.get()#True,100000000000) # workaround so that it can be keyboard interrupted
self.handle_response(response)
except socket.error:
print "socket error"
wallet.interface.is_connected = False
if self.wallet.was_updated:
self.wallet.gui_callback()
self.wallet.was_updated = False
response = self.interface.responses.get()
self.handle_response(response)
print "disconnected, gui callback"
self.wallet.gui_callback()
if self.loop:
time.sleep(5)
self.start_interface()

View File

@ -242,10 +242,11 @@ from interface import DEFAULT_SERVERS
class Wallet:
def __init__(self):
def __init__(self, gui_callback = lambda: None):
self.electrum_version = ELECTRUM_VERSION
self.seed_version = SEED_VERSION
self.gui_callback = gui_callback
self.gap_limit = 5 # configuration
self.fee = 100000
@ -339,7 +340,7 @@ class Wallet:
def new_seed(self, password):
seed = "%032x"%ecdsa.util.randrange( pow(2,128) )
self.init_mpk(seed)
#self.init_mpk(seed)
# encrypt
self.seed = self.pw_encode( seed, password )
@ -849,7 +850,8 @@ class Wallet:
return target, signing_addr, auth_name
def update_password(self, seed, new_password):
self.use_encryption = (new_password != '')
if new_password == '': new_password = None
self.use_encryption = (new_password != None)
self.seed = self.pw_encode( seed, new_password)
for k in self.imported_keys.keys():
a = self.imported_keys[k]