Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Index: ArmoryQt.py
- IDEA additional info:
- Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
- <+>UTF-8
- ===================================================================
- --- ArmoryQt.py (revision e1b435821967518f46f5d1722bf6245e5e011e29)
- +++ ArmoryQt.py (revision )
- @@ -32,6 +32,7 @@
- import platform
- import traceback
- import socket
- +import zlib
- from datetime import datetime
- # PyQt4 Imports
- @@ -122,14 +123,23 @@
- self.setupSystemTray()
- self.setupUriRegistration()
- + # Setup serial connection
- + self.setupSerialConnection()
- self.extraHeartbeatOnline = []
- self.extraHeartbeatAlways = []
- - self.lblArmoryStatus = QRichLabel('<font color=%s><i>Disconnected</i></font>' % \
- + self.lblArmoryStatus = QRichLabel('<font color=%s><i>Bitcoin Disconnected</i></font>' % \
- htmlColor('TextWarn'), doWrap=False)
- + self.lblArmoryStatus.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
- self.statusBar().insertPermanentWidget(0, self.lblArmoryStatus)
- +
- + self.lblSerialStatus = QRichLabel('<font color=%s><i>Serial Disconnected</i></font>' % \
- + htmlColor('TextWarn'), doWrap=False)
- + self.lblSerialStatus.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
- + self.statusBar().insertPermanentWidget(1, self.lblSerialStatus)
- +
- # Keep a persistent printer object for paper backups
- self.printer = QPrinter(QPrinter.HighResolution)
- self.printer.setPageSize(QPrinter.Letter)
- @@ -865,10 +875,36 @@
- SetValueEx(registryKey, name, 0, REG_SZ, val)
- CloseKey(registryKey)
- + class SerialMessageEmitter(QObject):
- + messageSignal = pyqtSignal(object)
- -
- +
- + def __init__(self, parent):
- + QObject.__init__(self, parent)
- -
- +
- + def messageReceived(self, message):
- + self.messageSignal.emit(message)
- + def register(self, receiver, method):
- + self.messageSignal.connect(method)
- + def unregister(self, receiver, method):
- + self.messageSignal.disconnect(method)
- +
- + def serialConnectionChanged(self, message):
- + if isinstance(message, bool):
- + if message:
- + self.lblSerialStatus.setText('<font color=%s>Serial Connected</font>' % htmlColor('TextGreen'))
- + else:
- + self.lblSerialStatus.setText('<font color=%s><i>Serial Disconnected</i></font>' % htmlColor('TextWarn'))
- +
- + def setupSerialConnection(self):
- + device = self.getSettingOrSetDefault('Serial_Device', '/dev/ttyUSB0')
- + rate = self.getSettingOrSetDefault('Serial_Rate', 115200)
- + self.serialEmitter = self.SerialMessageEmitter(self)
- + self.serialEmitter.register(self, self.serialConnectionChanged)
- + self.serialConnection = PySerialConnection(device, rate, self.serialEmitter.messageReceived)
- + self.serialConnection.open()
- +
- #############################################################################
- def execOfflineTx(self):
- dlgSelect = DlgOfflineSelect(self, self)
- @@ -1219,7 +1255,7 @@
- self.netMode = NETWORKMODE.Disconnected
- self.setDashboardDetails()
- self.lblArmoryStatus.setText( \
- - '<font color=%s><i>Disconnected</i></font>' % htmlColor('TextWarn'))
- + '<font color=%s><i>Bitcoin Disconnected</i></font>' % htmlColor('TextWarn'))
- if not self.getSettingOrSetDefault('NotifyDiscon', True):
- return
- @@ -1237,7 +1273,7 @@
- self.netMode = NETWORKMODE.Full
- self.setDashboardDetails()
- self.lblArmoryStatus.setText(\
- - '<font color=%s>Connected (%s blocks)</font> ' %
- + '<font color=%s>Bitcoin Connected (%s blocks)</font> ' %
- (htmlColor('TextGreen'), self.currBlockNum))
- if not self.getSettingOrSetDefault('NotifyReconn', True):
- return
- @@ -1631,7 +1667,7 @@
- self.statusBar().showMessage('Blockchain loaded, wallets sync\'d!', 10000)
- if self.netMode==NETWORKMODE.Full:
- self.lblArmoryStatus.setText(\
- - '<font color=%s>Connected (%s blocks)</font> ' %
- + '<font color=%s>Bitcoin Connected (%s blocks)</font> ' %
- (htmlColor('TextGreen'), self.currBlockNum))
- self.blkReceived = self.getSettingOrSetDefault('LastBlkRecvTime', 0)
- @@ -2431,6 +2467,8 @@
- self.execGetImportWltName()
- elif dlg.importType_paper:
- self.execRestorePaperBackup()
- + elif dlg.importType_serial:
- + self.execImportSerial()
- elif dlg.importType_migrate:
- self.execMigrateSatoshi()
- @@ -2580,7 +2618,106 @@
- self.addWalletToApplication(dlgPaper.newWallet, walletIsNew=False)
- #self.newWalletList.append([dlgPaper.newWallet, False])
- LOGINFO('Import Complete!')
- -
- +
- + def execImportSerial(self):
- + self.serialEmitter.register(self, self.receiveOnlineWalletList)
- + self.serialConnection.write(pb.OnlineWalletRequest())
- +
- + def receiveOnlineWalletList(self, response):
- + if isinstance(response, pb.OnlineWalletResponse):
- + self.serialEmitter.unregister(self, self.receiveOnlineWalletList)
- +
- + dlg = DlgImportSerialWallet(self, self, response)
- + if dlg.exec_():
- + id = str(dlg.combo.itemData(dlg.combo.currentIndex()).toString())
- +
- + if self.walletMap.has_key(id):
- + QMessageBox.warning(self, 'Duplicate Wallet!',\
- + 'You selected a wallet that has the same ID as one already '
- + 'in your wallet (%s)! If you would like to import it anyway, '
- + 'please delete the duplicate wallet in Armory, first.'% id,\
- + QMessageBox.Ok)
- + return
- +
- + self.serialEmitter.register(self, self.receiveOnlineWalletDetail)
- + request = pb.OnlineWalletRequest()
- + request.uniqueIDB58 = id
- + request.metadataOnly = False
- + self.serialConnection.write(request)
- +
- + # rough estimate of the wallet size, divided by the byte rate of the
- + # serial port multiplied by a fudge factor of 3
- + sleepTime = 65536 / (self.getSettingOrSetDefault("Serial_Rate", 115200) / 8) * 3
- +
- + DlgExecLongProcess(lambda: time.sleep(sleepTime),
- + 'Retrieving watching-only copy of '
- + '%s from offline device...' % id, self, self).exec_()
- +
- + def receiveOnlineWalletDetail(self, response):
- + if isinstance(response, pb.OnlineWalletResponse):
- + self.serialEmitter.unregister(self, self.receiveOnlineWalletDetail)
- +
- + id = (wlt.uniqueIDB58 for wlt in response.wallets).next()
- + wltData = (w for w in response.wallets if w.uniqueIDB58 == id).next()
- + unpacker = BinaryUnpacker(zlib.decompress(wltData.packedBytes))
- + newWlt = PyBtcWallet().readWalletData(unpacker, doScanNow=True, fileUpdate=False)
- + newWlt.fillAddressPool()
- + newWlt.walletPath = os.path.join(ARMORY_HOME_DIR, 'armory_%s_.watchonly.wallet' % newWlt.uniqueIDB58)
- + newWlt.writeFreshWalletFile(newWlt.getWalletPath())
- + newWlt.writeFreshWalletFile(newWlt.getWalletPath('backup'))
- +
- + if TheBDM.getBDMState() in ('Uninitialized', 'Offline'):
- + self.addWalletToApplication(newWlt, walletIsNew=False)
- + return
- +
- + if TheBDM.getBDMState()=='BlockchainReady':
- + doRescanNow = QMessageBox.question(self, 'Rescan Needed',\
- + 'The wallet was imported successfully, but cannot be displayed '
- + 'until the global transaction history is '
- + 'searched for previous transactions. This scan will potentially '
- + 'take much longer than a regular rescan, and the wallet cannot '
- + 'be shown on the main display until this rescan is complete.'
- + '<br><br>'
- + '<b>Would you like to go into offline mode to start this scan now?'
- + '</b> If you click "No" the scan will be aborted, and the wallet '
- + 'will not be added to Armory.',\
- + QMessageBox.Yes | QMessageBox.No)
- + else:
- + doRescanNow = QMessageBox.question(self, 'Rescan Needed',\
- + 'The wallet was imported successfully, but its balance cannot '
- + 'be determined until Armory performs a "recovery scan" for the '
- + 'wallet. This scan potentially takes much longer than a regular '
- + 'scan, and must be completed for all imported wallets. '
- + '<br><br>'
- + 'Armory is already in the middle of a scan and cannot be interrupted. '
- + 'Would you like to start the recovery scan when it is done?'
- + '<br><br>'
- + '</b> If you click "No," the wallet import will be aborted '
- + 'and you must re-import the wallet when you '
- + 'are able to wait for the recovery scan.',\
- + QMessageBox.Yes | QMessageBox.No)
- +
- + if doRescanNow == QMessageBox.Yes:
- + LOGINFO('User requested rescan after wallet import')
- + TheBDM.startWalletRecoveryScan(newWlt)
- + self.setDashboardDetails()
- + else:
- + LOGINFO('User aborted the wallet-import scan')
- + QMessageBox.warning(self, 'Import Failed',\
- + 'The wallet was not imported.', QMessageBox.Ok)
- +
- + # The wallet cannot exist without also being on disk.
- + # If the user aborted, we should remove the disk data.
- + thepath = newWlt.getWalletPath()
- + thepathBackup = newWlt.getWalletPath('backup')
- + os.remove(thepath)
- + os.remove(thepathBackup)
- + return
- +
- + #self.addWalletToApplication(newWlt, walletIsNew=False)
- + self.newWalletList.append([newWlt, False])
- + LOGINFO('Import Complete!')
- +
- #############################################################################
- def execMigrateSatoshi(self):
- reply = MsgBoxCustom(MSGBOX.Question, 'Wallet Version Warning', \
- @@ -3340,7 +3477,7 @@
- if self.netMode==NETWORKMODE.Full:
- self.lblArmoryStatus.setText(\
- - '<font color=%s>Connected (%s blocks)</font> ' % \
- + '<font color=%s>Bitcoin Connected (%s blocks)</font> ' % \
- (htmlColor('TextGreen'), self.currBlockNum))
- # Update the wallet view to immediately reflect new balances
- @@ -3496,6 +3633,7 @@
- elif doClose or moc=='Close':
- self.doShutdown = True
- TheBDM.execCleanShutdown(wait=False)
- + self.serialConnection.close()
- self.sysTray.hide()
- self.closeForReal(event)
- else:
- Index: serial.proto
- IDEA additional info:
- Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
- <+>UTF-8
- ===================================================================
- --- serial.proto (revision )
- +++ serial.proto (revision )
- @@ -0,0 +1,72 @@
- +message Heartbeat {
- + required string guid = 1;
- +}
- +
- +message SignatureRequest {
- + enum SignatureType {
- + PARTIAL = 1;
- + FULL = 2;
- + }
- +
- + required WalletIdentifer wallet = 1;
- + required SignatureType type = 2;
- + required string txDP = 3;
- + optional bool keepWalletUnlocked = 4;
- +}
- +
- +message SignatureResponse {
- + required string txDP = 1;
- +}
- +
- +message OnlineWalletRequest {
- + optional string uniqueIDB58 = 1;
- + optional bool metadataOnly = 2 [default = true];
- +}
- +
- +message OnlineWalletResponse {
- + repeated OnlineWallet wallets = 1;
- +}
- +
- +message InputRequest {
- + required string prompt = 1;
- + optional string validResponseCharacters = 2;
- +}
- +
- +message InputResponse {
- + required string input = 1;
- +}
- +
- +message Notification {
- + enum NotificationType {
- + INFORMATION = 1;
- + ERROR = 2;
- + CRITICAL_ERROR = 3;
- + }
- +
- + required NotificationType type = 1;
- + required string message = 2;
- +}
- +
- +message CreateWallet {
- + required string name = 1;
- + optional string description = 2;
- + required bool useEncryption = 3;
- + optional int32 targetComputeTime = 4;
- + optional int32 maxMemoryUsage = 5;
- +}
- +
- +message Reset {
- + required bool shutdown = 1;
- +}
- +
- +message WalletIdentifer {
- + required string uniqueIDB58 = 1;
- + required int32 lastComputedChainIndex = 2;
- +}
- +
- +message OnlineWallet {
- + required string uniqueIDB58 = 1;
- + required string labelName = 2;
- + required string labelDescr = 3;
- + optional bytes packedBytes = 4;
- +}
- \ No newline at end of file
- Index: serial_pb2.py
- IDEA additional info:
- Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
- <+>UTF-8
- ===================================================================
- --- serial_pb2.py (revision )
- +++ serial_pb2.py (revision )
- @@ -0,0 +1,591 @@
- +# Generated by the protocol buffer compiler. DO NOT EDIT!
- +
- +from google.protobuf import descriptor
- +from google.protobuf import message
- +from google.protobuf import reflection
- +from google.protobuf import descriptor_pb2
- +# @@protoc_insertion_point(imports)
- +
- +
- +
- +DESCRIPTOR = descriptor.FileDescriptor(
- + name='serial.proto',
- + package='',
- + serialized_pb='\n\x0cserial.proto\"\x19\n\tHeartbeat\x12\x0c\n\x04guid\x18\x01 \x02(\t\"\xb5\x01\n\x10SignatureRequest\x12 \n\x06wallet\x18\x01 \x02(\x0b\x32\x10.WalletIdentifer\x12-\n\x04type\x18\x02 \x02(\x0e\x32\x1f.SignatureRequest.SignatureType\x12\x0c\n\x04txDP\x18\x03 \x02(\t\x12\x1a\n\x12keepWalletUnlocked\x18\x04 \x01(\x08\"&\n\rSignatureType\x12\x0b\n\x07PARTIAL\x10\x01\x12\x08\n\x04\x46ULL\x10\x02\"!\n\x11SignatureResponse\x12\x0c\n\x04txDP\x18\x01 \x02(\t\"F\n\x13OnlineWalletRequest\x12\x13\n\x0buniqueIDB58\x18\x01 \x01(\t\x12\x1a\n\x0cmetadataOnly\x18\x02 \x01(\x08:\x04true\"6\n\x14OnlineWalletResponse\x12\x1e\n\x07wallets\x18\x01 \x03(\x0b\x32\r.OnlineWallet\"?\n\x0cInputRequest\x12\x0e\n\x06prompt\x18\x01 \x02(\t\x12\x1f\n\x17validResponseCharacters\x18\x02 \x01(\t\"\x1e\n\rInputResponse\x12\r\n\x05input\x18\x01 \x02(\t\"\x91\x01\n\x0cNotification\x12,\n\x04type\x18\x01 \x02(\x0e\x32\x1e.Notification.NotificationType\x12\x0f\n\x07message\x18\x02 \x02(\t\"B\n\x10NotificationType\x12\x0f\n\x0bINFORMATION\x10\x01\x12\t\n\x05\x45RROR\x10\x02\x12\x12\n\x0e\x43RITICAL_ERROR\x10\x03\"{\n\x0c\x43reateWallet\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x15\n\ruseEncryption\x18\x03 \x02(\x08\x12\x19\n\x11targetComputeTime\x18\x04 \x01(\x05\x12\x16\n\x0emaxMemoryUsage\x18\x05 \x01(\x05\"\x19\n\x05Reset\x12\x10\n\x08shutdown\x18\x01 \x02(\x08\"F\n\x0fWalletIdentifer\x12\x13\n\x0buniqueIDB58\x18\x01 \x02(\t\x12\x1e\n\x16lastComputedChainIndex\x18\x02 \x02(\x05\"_\n\x0cOnlineWallet\x12\x13\n\x0buniqueIDB58\x18\x01 \x02(\t\x12\x11\n\tlabelName\x18\x02 \x02(\t\x12\x12\n\nlabelDescr\x18\x03 \x02(\t\x12\x13\n\x0bpackedBytes\x18\x04 \x01(\x0c')
- +
- +
- +
- +_SIGNATUREREQUEST_SIGNATURETYPE = descriptor.EnumDescriptor(
- + name='SignatureType',
- + full_name='SignatureRequest.SignatureType',
- + filename=None,
- + file=DESCRIPTOR,
- + values=[
- + descriptor.EnumValueDescriptor(
- + name='PARTIAL', index=0, number=1,
- + options=None,
- + type=None),
- + descriptor.EnumValueDescriptor(
- + name='FULL', index=1, number=2,
- + options=None,
- + type=None),
- + ],
- + containing_type=None,
- + options=None,
- + serialized_start=187,
- + serialized_end=225,
- +)
- +
- +_NOTIFICATION_NOTIFICATIONTYPE = descriptor.EnumDescriptor(
- + name='NotificationType',
- + full_name='Notification.NotificationType',
- + filename=None,
- + file=DESCRIPTOR,
- + values=[
- + descriptor.EnumValueDescriptor(
- + name='INFORMATION', index=0, number=1,
- + options=None,
- + type=None),
- + descriptor.EnumValueDescriptor(
- + name='ERROR', index=1, number=2,
- + options=None,
- + type=None),
- + descriptor.EnumValueDescriptor(
- + name='CRITICAL_ERROR', index=2, number=3,
- + options=None,
- + type=None),
- + ],
- + containing_type=None,
- + options=None,
- + serialized_start=567,
- + serialized_end=633,
- +)
- +
- +
- +_HEARTBEAT = descriptor.Descriptor(
- + name='Heartbeat',
- + full_name='Heartbeat',
- + filename=None,
- + file=DESCRIPTOR,
- + containing_type=None,
- + fields=[
- + descriptor.FieldDescriptor(
- + name='guid', full_name='Heartbeat.guid', index=0,
- + number=1, type=9, cpp_type=9, label=2,
- + has_default_value=False, default_value=unicode("", "utf-8"),
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + ],
- + extensions=[
- + ],
- + nested_types=[],
- + enum_types=[
- + ],
- + options=None,
- + is_extendable=False,
- + extension_ranges=[],
- + serialized_start=16,
- + serialized_end=41,
- +)
- +
- +
- +_SIGNATUREREQUEST = descriptor.Descriptor(
- + name='SignatureRequest',
- + full_name='SignatureRequest',
- + filename=None,
- + file=DESCRIPTOR,
- + containing_type=None,
- + fields=[
- + descriptor.FieldDescriptor(
- + name='wallet', full_name='SignatureRequest.wallet', index=0,
- + number=1, type=11, cpp_type=10, label=2,
- + has_default_value=False, default_value=None,
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + descriptor.FieldDescriptor(
- + name='type', full_name='SignatureRequest.type', index=1,
- + number=2, type=14, cpp_type=8, label=2,
- + has_default_value=False, default_value=1,
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + descriptor.FieldDescriptor(
- + name='txDP', full_name='SignatureRequest.txDP', index=2,
- + number=3, type=9, cpp_type=9, label=2,
- + has_default_value=False, default_value=unicode("", "utf-8"),
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + descriptor.FieldDescriptor(
- + name='keepWalletUnlocked', full_name='SignatureRequest.keepWalletUnlocked', index=3,
- + number=4, type=8, cpp_type=7, label=1,
- + has_default_value=False, default_value=False,
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + ],
- + extensions=[
- + ],
- + nested_types=[],
- + enum_types=[
- + _SIGNATUREREQUEST_SIGNATURETYPE,
- + ],
- + options=None,
- + is_extendable=False,
- + extension_ranges=[],
- + serialized_start=44,
- + serialized_end=225,
- +)
- +
- +
- +_SIGNATURERESPONSE = descriptor.Descriptor(
- + name='SignatureResponse',
- + full_name='SignatureResponse',
- + filename=None,
- + file=DESCRIPTOR,
- + containing_type=None,
- + fields=[
- + descriptor.FieldDescriptor(
- + name='txDP', full_name='SignatureResponse.txDP', index=0,
- + number=1, type=9, cpp_type=9, label=2,
- + has_default_value=False, default_value=unicode("", "utf-8"),
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + ],
- + extensions=[
- + ],
- + nested_types=[],
- + enum_types=[
- + ],
- + options=None,
- + is_extendable=False,
- + extension_ranges=[],
- + serialized_start=227,
- + serialized_end=260,
- +)
- +
- +
- +_ONLINEWALLETREQUEST = descriptor.Descriptor(
- + name='OnlineWalletRequest',
- + full_name='OnlineWalletRequest',
- + filename=None,
- + file=DESCRIPTOR,
- + containing_type=None,
- + fields=[
- + descriptor.FieldDescriptor(
- + name='uniqueIDB58', full_name='OnlineWalletRequest.uniqueIDB58', index=0,
- + number=1, type=9, cpp_type=9, label=1,
- + has_default_value=False, default_value=unicode("", "utf-8"),
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + descriptor.FieldDescriptor(
- + name='metadataOnly', full_name='OnlineWalletRequest.metadataOnly', index=1,
- + number=2, type=8, cpp_type=7, label=1,
- + has_default_value=True, default_value=True,
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + ],
- + extensions=[
- + ],
- + nested_types=[],
- + enum_types=[
- + ],
- + options=None,
- + is_extendable=False,
- + extension_ranges=[],
- + serialized_start=262,
- + serialized_end=332,
- +)
- +
- +
- +_ONLINEWALLETRESPONSE = descriptor.Descriptor(
- + name='OnlineWalletResponse',
- + full_name='OnlineWalletResponse',
- + filename=None,
- + file=DESCRIPTOR,
- + containing_type=None,
- + fields=[
- + descriptor.FieldDescriptor(
- + name='wallets', full_name='OnlineWalletResponse.wallets', index=0,
- + number=1, type=11, cpp_type=10, label=3,
- + has_default_value=False, default_value=[],
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + ],
- + extensions=[
- + ],
- + nested_types=[],
- + enum_types=[
- + ],
- + options=None,
- + is_extendable=False,
- + extension_ranges=[],
- + serialized_start=334,
- + serialized_end=388,
- +)
- +
- +
- +_INPUTREQUEST = descriptor.Descriptor(
- + name='InputRequest',
- + full_name='InputRequest',
- + filename=None,
- + file=DESCRIPTOR,
- + containing_type=None,
- + fields=[
- + descriptor.FieldDescriptor(
- + name='prompt', full_name='InputRequest.prompt', index=0,
- + number=1, type=9, cpp_type=9, label=2,
- + has_default_value=False, default_value=unicode("", "utf-8"),
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + descriptor.FieldDescriptor(
- + name='validResponseCharacters', full_name='InputRequest.validResponseCharacters', index=1,
- + number=2, type=9, cpp_type=9, label=1,
- + has_default_value=False, default_value=unicode("", "utf-8"),
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + ],
- + extensions=[
- + ],
- + nested_types=[],
- + enum_types=[
- + ],
- + options=None,
- + is_extendable=False,
- + extension_ranges=[],
- + serialized_start=390,
- + serialized_end=453,
- +)
- +
- +
- +_INPUTRESPONSE = descriptor.Descriptor(
- + name='InputResponse',
- + full_name='InputResponse',
- + filename=None,
- + file=DESCRIPTOR,
- + containing_type=None,
- + fields=[
- + descriptor.FieldDescriptor(
- + name='input', full_name='InputResponse.input', index=0,
- + number=1, type=9, cpp_type=9, label=2,
- + has_default_value=False, default_value=unicode("", "utf-8"),
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + ],
- + extensions=[
- + ],
- + nested_types=[],
- + enum_types=[
- + ],
- + options=None,
- + is_extendable=False,
- + extension_ranges=[],
- + serialized_start=455,
- + serialized_end=485,
- +)
- +
- +
- +_NOTIFICATION = descriptor.Descriptor(
- + name='Notification',
- + full_name='Notification',
- + filename=None,
- + file=DESCRIPTOR,
- + containing_type=None,
- + fields=[
- + descriptor.FieldDescriptor(
- + name='type', full_name='Notification.type', index=0,
- + number=1, type=14, cpp_type=8, label=2,
- + has_default_value=False, default_value=1,
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + descriptor.FieldDescriptor(
- + name='message', full_name='Notification.message', index=1,
- + number=2, type=9, cpp_type=9, label=2,
- + has_default_value=False, default_value=unicode("", "utf-8"),
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + ],
- + extensions=[
- + ],
- + nested_types=[],
- + enum_types=[
- + _NOTIFICATION_NOTIFICATIONTYPE,
- + ],
- + options=None,
- + is_extendable=False,
- + extension_ranges=[],
- + serialized_start=488,
- + serialized_end=633,
- +)
- +
- +
- +_CREATEWALLET = descriptor.Descriptor(
- + name='CreateWallet',
- + full_name='CreateWallet',
- + filename=None,
- + file=DESCRIPTOR,
- + containing_type=None,
- + fields=[
- + descriptor.FieldDescriptor(
- + name='name', full_name='CreateWallet.name', index=0,
- + number=1, type=9, cpp_type=9, label=2,
- + has_default_value=False, default_value=unicode("", "utf-8"),
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + descriptor.FieldDescriptor(
- + name='description', full_name='CreateWallet.description', index=1,
- + number=2, type=9, cpp_type=9, label=1,
- + has_default_value=False, default_value=unicode("", "utf-8"),
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + descriptor.FieldDescriptor(
- + name='useEncryption', full_name='CreateWallet.useEncryption', index=2,
- + number=3, type=8, cpp_type=7, label=2,
- + has_default_value=False, default_value=False,
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + descriptor.FieldDescriptor(
- + name='targetComputeTime', full_name='CreateWallet.targetComputeTime', index=3,
- + number=4, type=5, cpp_type=1, label=1,
- + has_default_value=False, default_value=0,
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + descriptor.FieldDescriptor(
- + name='maxMemoryUsage', full_name='CreateWallet.maxMemoryUsage', index=4,
- + number=5, type=5, cpp_type=1, label=1,
- + has_default_value=False, default_value=0,
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + ],
- + extensions=[
- + ],
- + nested_types=[],
- + enum_types=[
- + ],
- + options=None,
- + is_extendable=False,
- + extension_ranges=[],
- + serialized_start=635,
- + serialized_end=758,
- +)
- +
- +
- +_RESET = descriptor.Descriptor(
- + name='Reset',
- + full_name='Reset',
- + filename=None,
- + file=DESCRIPTOR,
- + containing_type=None,
- + fields=[
- + descriptor.FieldDescriptor(
- + name='shutdown', full_name='Reset.shutdown', index=0,
- + number=1, type=8, cpp_type=7, label=2,
- + has_default_value=False, default_value=False,
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + ],
- + extensions=[
- + ],
- + nested_types=[],
- + enum_types=[
- + ],
- + options=None,
- + is_extendable=False,
- + extension_ranges=[],
- + serialized_start=760,
- + serialized_end=785,
- +)
- +
- +
- +_WALLETIDENTIFER = descriptor.Descriptor(
- + name='WalletIdentifer',
- + full_name='WalletIdentifer',
- + filename=None,
- + file=DESCRIPTOR,
- + containing_type=None,
- + fields=[
- + descriptor.FieldDescriptor(
- + name='uniqueIDB58', full_name='WalletIdentifer.uniqueIDB58', index=0,
- + number=1, type=9, cpp_type=9, label=2,
- + has_default_value=False, default_value=unicode("", "utf-8"),
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + descriptor.FieldDescriptor(
- + name='lastComputedChainIndex', full_name='WalletIdentifer.lastComputedChainIndex', index=1,
- + number=2, type=5, cpp_type=1, label=2,
- + has_default_value=False, default_value=0,
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + ],
- + extensions=[
- + ],
- + nested_types=[],
- + enum_types=[
- + ],
- + options=None,
- + is_extendable=False,
- + extension_ranges=[],
- + serialized_start=787,
- + serialized_end=857,
- +)
- +
- +
- +_ONLINEWALLET = descriptor.Descriptor(
- + name='OnlineWallet',
- + full_name='OnlineWallet',
- + filename=None,
- + file=DESCRIPTOR,
- + containing_type=None,
- + fields=[
- + descriptor.FieldDescriptor(
- + name='uniqueIDB58', full_name='OnlineWallet.uniqueIDB58', index=0,
- + number=1, type=9, cpp_type=9, label=2,
- + has_default_value=False, default_value=unicode("", "utf-8"),
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + descriptor.FieldDescriptor(
- + name='labelName', full_name='OnlineWallet.labelName', index=1,
- + number=2, type=9, cpp_type=9, label=2,
- + has_default_value=False, default_value=unicode("", "utf-8"),
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + descriptor.FieldDescriptor(
- + name='labelDescr', full_name='OnlineWallet.labelDescr', index=2,
- + number=3, type=9, cpp_type=9, label=2,
- + has_default_value=False, default_value=unicode("", "utf-8"),
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + descriptor.FieldDescriptor(
- + name='packedBytes', full_name='OnlineWallet.packedBytes', index=3,
- + number=4, type=12, cpp_type=9, label=1,
- + has_default_value=False, default_value="",
- + message_type=None, enum_type=None, containing_type=None,
- + is_extension=False, extension_scope=None,
- + options=None),
- + ],
- + extensions=[
- + ],
- + nested_types=[],
- + enum_types=[
- + ],
- + options=None,
- + is_extendable=False,
- + extension_ranges=[],
- + serialized_start=859,
- + serialized_end=954,
- +)
- +
- +_SIGNATUREREQUEST.fields_by_name['wallet'].message_type = _WALLETIDENTIFER
- +_SIGNATUREREQUEST.fields_by_name['type'].enum_type = _SIGNATUREREQUEST_SIGNATURETYPE
- +_SIGNATUREREQUEST_SIGNATURETYPE.containing_type = _SIGNATUREREQUEST;
- +_ONLINEWALLETRESPONSE.fields_by_name['wallets'].message_type = _ONLINEWALLET
- +_NOTIFICATION.fields_by_name['type'].enum_type = _NOTIFICATION_NOTIFICATIONTYPE
- +_NOTIFICATION_NOTIFICATIONTYPE.containing_type = _NOTIFICATION;
- +DESCRIPTOR.message_types_by_name['Heartbeat'] = _HEARTBEAT
- +DESCRIPTOR.message_types_by_name['SignatureRequest'] = _SIGNATUREREQUEST
- +DESCRIPTOR.message_types_by_name['SignatureResponse'] = _SIGNATURERESPONSE
- +DESCRIPTOR.message_types_by_name['OnlineWalletRequest'] = _ONLINEWALLETREQUEST
- +DESCRIPTOR.message_types_by_name['OnlineWalletResponse'] = _ONLINEWALLETRESPONSE
- +DESCRIPTOR.message_types_by_name['InputRequest'] = _INPUTREQUEST
- +DESCRIPTOR.message_types_by_name['InputResponse'] = _INPUTRESPONSE
- +DESCRIPTOR.message_types_by_name['Notification'] = _NOTIFICATION
- +DESCRIPTOR.message_types_by_name['CreateWallet'] = _CREATEWALLET
- +DESCRIPTOR.message_types_by_name['Reset'] = _RESET
- +DESCRIPTOR.message_types_by_name['WalletIdentifer'] = _WALLETIDENTIFER
- +DESCRIPTOR.message_types_by_name['OnlineWallet'] = _ONLINEWALLET
- +
- +class Heartbeat(message.Message):
- + __metaclass__ = reflection.GeneratedProtocolMessageType
- + DESCRIPTOR = _HEARTBEAT
- +
- + # @@protoc_insertion_point(class_scope:Heartbeat)
- +
- +class SignatureRequest(message.Message):
- + __metaclass__ = reflection.GeneratedProtocolMessageType
- + DESCRIPTOR = _SIGNATUREREQUEST
- +
- + # @@protoc_insertion_point(class_scope:SignatureRequest)
- +
- +class SignatureResponse(message.Message):
- + __metaclass__ = reflection.GeneratedProtocolMessageType
- + DESCRIPTOR = _SIGNATURERESPONSE
- +
- + # @@protoc_insertion_point(class_scope:SignatureResponse)
- +
- +class OnlineWalletRequest(message.Message):
- + __metaclass__ = reflection.GeneratedProtocolMessageType
- + DESCRIPTOR = _ONLINEWALLETREQUEST
- +
- + # @@protoc_insertion_point(class_scope:OnlineWalletRequest)
- +
- +class OnlineWalletResponse(message.Message):
- + __metaclass__ = reflection.GeneratedProtocolMessageType
- + DESCRIPTOR = _ONLINEWALLETRESPONSE
- +
- + # @@protoc_insertion_point(class_scope:OnlineWalletResponse)
- +
- +class InputRequest(message.Message):
- + __metaclass__ = reflection.GeneratedProtocolMessageType
- + DESCRIPTOR = _INPUTREQUEST
- +
- + # @@protoc_insertion_point(class_scope:InputRequest)
- +
- +class InputResponse(message.Message):
- + __metaclass__ = reflection.GeneratedProtocolMessageType
- + DESCRIPTOR = _INPUTRESPONSE
- +
- + # @@protoc_insertion_point(class_scope:InputResponse)
- +
- +class Notification(message.Message):
- + __metaclass__ = reflection.GeneratedProtocolMessageType
- + DESCRIPTOR = _NOTIFICATION
- +
- + # @@protoc_insertion_point(class_scope:Notification)
- +
- +class CreateWallet(message.Message):
- + __metaclass__ = reflection.GeneratedProtocolMessageType
- + DESCRIPTOR = _CREATEWALLET
- +
- + # @@protoc_insertion_point(class_scope:CreateWallet)
- +
- +class Reset(message.Message):
- + __metaclass__ = reflection.GeneratedProtocolMessageType
- + DESCRIPTOR = _RESET
- +
- + # @@protoc_insertion_point(class_scope:Reset)
- +
- +class WalletIdentifer(message.Message):
- + __metaclass__ = reflection.GeneratedProtocolMessageType
- + DESCRIPTOR = _WALLETIDENTIFER
- +
- + # @@protoc_insertion_point(class_scope:WalletIdentifer)
- +
- +class OnlineWallet(message.Message):
- + __metaclass__ = reflection.GeneratedProtocolMessageType
- + DESCRIPTOR = _ONLINEWALLET
- +
- + # @@protoc_insertion_point(class_scope:OnlineWallet)
- +
- +# @@protoc_insertion_point(module_scope)
- Index: qtdialogs.py
- IDEA additional info:
- Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
- <+>UTF-8
- ===================================================================
- --- qtdialogs.py (revision e1b435821967518f46f5d1722bf6245e5e011e29)
- +++ qtdialogs.py (revision )
- @@ -15,6 +15,7 @@
- from armorymodels import *
- from armorycolors import Colors, htmlColor
- import qrc_img_resources
- +import serial_pb2 as pb
- MIN_PASSWD_WIDTH = lambda obj: tightSizeStr(obj, '*'*16)[0]
- @@ -2478,12 +2479,15 @@
- lblImportDescr = QLabel('Chose the wallet import source:')
- self.btnImportFile = QPushButton("Import Armory wallet from &file")
- self.btnImportPaper = QPushButton("Restore from &paper backup")
- + self.btnImportSerial = QPushButton("Import Armory wallet from &serial")
- + self.btnImportSerial.setEnabled(main.serialConnection.isConnected)
- self.btnMigrate = QPushButton("Migrate wallet.dat (main Bitcoin App)")
- self.btnImportFile.setMinimumWidth(300)
- self.connect( self.btnImportFile, SIGNAL("clicked()"), self.acceptImport)
- self.connect( self.btnImportPaper, SIGNAL('clicked()'), self.acceptPaper)
- + self.connect( self.btnImportSerial, SIGNAL('clicked()'), self.acceptSerial)
- self.connect( self.btnMigrate, SIGNAL('clicked()'), self.acceptMigrate)
- ttip1 = createToolTipObject('Import an existing Armory wallet, usually with a '
- @@ -2495,12 +2499,16 @@
- 'a wallet, you can manually enter the wallet '
- 'data into Armory to recover the wallet.')
- + ttip3 = createToolTipObject('If you have an offline Armory server connected '
- + 'through a serial port, you can import the '
- + 'offline copy of any wallets there.')
- +
- ttip3 = createToolTipObject('Migrate all your wallet.dat addresses '
- 'from the regular Bitcoin client to an Armory '
- 'wallet.')
- w,h = relaxedSizeStr(ttip1, '(?)')
- - for ttip in (ttip1, ttip2):
- + for ttip in (ttip1, ttip2, ttip3):
- ttip.setMaximumSize(w,h)
- ttip.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
- @@ -2509,7 +2517,8 @@
- layout.addWidget(lblImportDescr, 0,0, 1, 2)
- layout.addWidget(self.btnImportFile, 1,0, 1, 2); layout.addWidget(ttip1, 1,2,1,1)
- layout.addWidget(self.btnImportPaper, 2,0, 1, 2); layout.addWidget(ttip2, 2,2,1,1)
- - #layout.addWidget(self.btnMigrate, 3,0, 1, 2); layout.addWidget(ttip3, 3,2,1,1)
- + layout.addWidget(self.btnImportSerial,3,0, 1, 2); layout.addWidget(ttip3, 3,2,1,1)
- + #layout.addWidget(self.btnMigrate, 4,0, 1, 2); layout.addWidget(ttip4, 4,2,1,1)
- if self.main.usermode in (USERMODE.Advanced, USERMODE.Expert):
- lbl = QRichLabel('You can manually add wallets to armory by copying them '
- @@ -2538,12 +2547,21 @@
- def acceptPaper(self):
- self.importType_file = False
- self.importType_paper = True
- + self.importType_serial = False
- self.importType_migrate = False
- self.accept()
- -
- +
- + def acceptSerial(self):
- + self.importType_file = False
- + self.importType_paper = False
- + self.importType_serial = True
- + self.importType_migrate = False
- + self.accept()
- +
- def acceptMigrate(self):
- self.importType_file = False
- self.importType_paper = False
- + self.importType_serial = False
- self.importType_migrate = True
- self.accept()
- @@ -3783,10 +3801,34 @@
- # Will pop up a little "please wait..." window while filling addr pool
- DlgExecLongProcess(fillAddrPoolAndAccept, "Recovering wallet...", self, self.main).exec_()
- -
- +
- +class DlgImportSerialWallet(ArmoryDialog):
- + def __init__(self, parent=None, main=None, response=None):
- + super(DlgImportSerialWallet, self).__init__(parent, main)
- + self.setWindowTitle('Import Wallet From Serial Device')
- + self.setWindowIcon(QIcon(self.main.iconfile))
- + lblImportDescr = QLabel('Choose the wallet to import:')
- + self.combo = QComboBox(self)
- + for wlt in response.wallets:
- + self.combo.addItem("%s: %s (%s)" % (wlt.uniqueIDB58, wlt.labelName, wlt.labelDescr), wlt.uniqueIDB58)
- + h,w = relaxedSizeNChar(self, 50)
- + self.combo.setMinimumSize(h,w)
- +
- + buttonbox = QDialogButtonBox(QDialogButtonBox.Ok |\
- + QDialogButtonBox.Cancel)
- + self.connect(buttonbox, SIGNAL('accepted()'), self.accept)
- + self.connect(buttonbox, SIGNAL('rejected()'), self.reject)
- +
- + # Set up the layout
- + layout = QVBoxLayout()
- + layout.addWidget(lblImportDescr)
- + layout.addWidget(self.combo)
- + layout.addWidget(buttonbox)
- + self.setLayout(layout)
- +
- ################################################################################
- class DlgSetComment(ArmoryDialog):
- """ This will be a dumb dialog for retrieving a comment from user """
- @@ -5644,6 +5686,8 @@
- self.txdp = txdp
- self.wlt = wlt
- + self.main.serialEmitter.unregister(self.receiveSerialMessage)
- +
- canSign = False
- lblDescr = QRichLabel('')
- if determineWalletType(wlt, self.main)[0]==WLTTYPES.Offline:
- @@ -5716,6 +5760,17 @@
- 'Copy the transaction data to the clipboard, so that it can be '
- 'pasted into an email or a text document.')
- + self.btnTransmitStart = QPushButton('Transmit over serial')
- + self.btnTransmitStart.setEnabled(self.main.serialConnection.isConnected)
- + self.connect(self.btnTransmitStart, SIGNAL('clicked()'), self.doTxSerialTransmit)
- + ttipTransmit = createToolTipObject( \
- + 'Transmit the transaction data over the configured serial connection '
- + 'and process the received signed transaction.')
- +
- + self.btnTransmitCancel = QPushButton('Cancel transmission')
- + self.btnTransmitCancel.setVisible(False)
- + self.connect(self.btnTransmitCancel, SIGNAL('clicked()'), self.cancelTxSerialTransmit, True)
- +
- lblInstruct = QRichLabel('<b>Instructions for completing this transaction:</b>')
- lblUTX = QRichLabel('<b>Transaction Data</b> \t (Unsigned ID: %s)' % txdp.uniqueB58)
- w,h = tightSizeStr(GETFONT('Fixed',8),'0'*85)[0], int(12*8.2)
- @@ -5759,14 +5814,17 @@
- frmLowerLayout = QGridLayout()
- frmLowerLayout.addWidget(frmUTX, 0,0, 1,3)
- - frmLowerLayout.addWidget(self.txtTxDP, 1,0, 3,1)
- + frmLowerLayout.addWidget(self.txtTxDP, 1,0, 4,1)
- frmLowerLayout.addWidget(btnSave, 1,1, 1,1)
- frmLowerLayout.addWidget(ttipSave, 1,2, 1,1)
- frmLowerLayout.addWidget(btnCopy, 2,1, 1,1)
- frmLowerLayout.addWidget(ttipCopy, 2,2, 1,1)
- - frmLowerLayout.addWidget(self.lblCopied,3,1, 1,2)
- + frmLowerLayout.addWidget(self.btnTransmitStart, 3,1, 1,1)
- + frmLowerLayout.addWidget(self.btnTransmitCancel, 3,2, 1,1)
- + frmLowerLayout.addWidget(ttipTransmit, 3,3, 1,1)
- + frmLowerLayout.addWidget(self.lblCopied,4,1, 1,2)
- - frmLowerLayout.addWidget(nextStepStrip, 4,0, 1,3)
- + frmLowerLayout.addWidget(nextStepStrip, 5,0, 1,3)
- frmLower.setLayout(frmLowerLayout)
- @@ -5796,6 +5854,49 @@
- clipb.setText(self.txtSigned.toPlainText())
- self.lblCopiedS.setText('<i>Copied!</i>')
- + def receiveSerialMessage(self, msg):
- + if isinstance(msg, bool):
- + self.btnTransmitStart.setEnabled(msg)
- + elif isinstance(msg, pb.Notification):
- + self.lblCopied.setText('<i>%s</i>' % msg.message)
- + elif isinstance(msg, pb.Error):
- + self.lblCopied.setText('<font color="%s">%s</font>' % (msg.message, htmlColor("TextWarn")))
- + if msg.critical:
- + self.cancelTxSerialTransmit(False)
- + elif isinstance(msg, pb.SignatureResponse):
- + self.cancelTxSerialTransmit(False)
- +
- + if msg.signatureType == msg.FULL:
- + dlgRvw = DlgReviewOfflineTx(self.parent, self.main)
- + dlgRvw.txtTxDP.setText(msg.txDP)
- + dlgRvw.exec_()
- + self.accept()
- + #else:
- + # transaction signed by some, but not all of the necessary private keys
- +
- + def cancelTxSerialTransmit(self, reset):
- + if reset:
- + msg = pb.Reset()
- + msg.shutdown = False
- + self.main.serialConnection.write(msg)
- +
- + self.btnTransmitStart.setVisible(False)
- + self.btnTransmitCancel.setVisible(True)
- + self.lblCopied.clear()
- +
- + def doTxSerialTransmit(self):
- + """ Send the Unsigned-Tx block of data to TODO: """
- + self.btnTransmitStart.setVisible(False)
- + self.btnTransmitCancel.setVisible(True)
- +
- + msg = pb.SignatureRequest()
- + msg.wallet.uniqueIDB58 = self.wlt.uniqueIDB58
- + msg.wallet.lastComputedChainIndex = self.wlt.lastComputedChainIndex
- + msg.txDP = str(self.txtTxDP.toPlainText())
- +
- + self.main.serialConnection.write(msg)
- + self.lblCopied.setText('Sent unsigned tx')
- +
- def doSaveFile(self):
- """ Save the Unsigned-Tx block of data """
- dpid = self.txdp.uniqueB58
- @@ -5925,7 +6026,13 @@
- DlgReviewOfflineTx(self, self.main).exec_()
- self.accept()
- + def accept(self, *args):
- + self.main.serialEmitter.unregister(self.receiveSerialMessage)
- + super(DlgOfflineTxCreated, self).accept(*args)
- + def reject(self, *args):
- + self.main.serialEmitter.unregister(self.receiveSerialMessage)
- + super(DlgOfflineTxCreated, self).reject(*args)
- ################################################################################
- class DlgOfflineSelect(ArmoryDialog):
- Index: offline_serial_server.py
- IDEA additional info:
- Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
- <+>UTF-8
- ===================================================================
- --- offline_serial_server.py (revision )
- +++ offline_serial_server.py (revision )
- @@ -0,0 +1,180 @@
- +import sys
- +from twisted.python.dist import build_scripts_twisted
- +
- +sys.path.append('..')
- +
- +from armoryengine import *
- +import os
- +import time
- +import getpass
- +import zlib
- +from serial_pb2 import *
- +
- +class OfflineSerialServer:
- + def __init__(self):
- + if len(CLI_ARGS) < 2:
- + print 'USAGE: %s <serial device> <serial rate>' % argv[0]
- + exit()
- +
- + self.wallets = dict([self.loadWallet(wlt) for wlt in os.listdir(ARMORY_HOME_DIR) if
- + wlt.endswith('.wallet') and not wlt.endswith('_backup.wallet')])
- +
- + self.device = CLI_ARGS[0]
- + self.rate = int(CLI_ARGS[1])
- +
- + self.conn = PySerialConnection(self.device, self.rate, self.receiveMessage)
- +
- + def __enter__(self):
- + return self
- +
- + def __exit__(self, exc_type, exc_val, exc_tb):
- + self.conn.close()
- + if self.conn.port.isOpen():
- + self.conn.port.close()
- + print 'Serial port closed'
- +
- + def writeInfo(self, info):
- + msg = Notification()
- + msg.type = msg.INFORMATION
- + msg.message = info
- + self.conn.write(msg)
- +
- + def writeError(self, error, critical=False):
- + msg = Notification()
- + if critical:
- + msg.type = msg.CRITICAL_ERROR
- + else:
- + msg.type = msg.ERROR
- + msg.message = error
- + self.conn.write(msg)
- +
- + def loadWallet(self, file):
- + wlt = PyBtcWallet().readWalletFile(os.path.join(ARMORY_HOME_DIR, file))
- + print 'Loaded wallet %s (%s)' % (wlt.labelName, wlt.uniqueIDB58)
- + return (wlt.uniqueIDB58, wlt)
- +
- + def run(self):
- + self.conn.open()
- + self.conn.heartbeat()
- + print 'Serial port open'
- +
- + def extendWallet(self, wlt, index):
- + while wlt.lastComputedChainIndex < index:
- + wlt.computeNextAddress()
- +
- + def unlockWallet(self, wlt):
- + # If the wallet is encrypted, get the passphrase
- + if wlt.useEncryption and wlt.isLocked:
- + for ntries in range(3):
- + self.writeInfo('Wallet passphrase required')
- + passwd = SecureBinaryData(getpass.getpass('Wallet Passphrase: '))
- + if wlt.verifyPassphrase(passwd):
- + self.writeInfo('Unlocking wallet')
- + break;
- + else:
- + self.writeError('Incorrect passphrase')
- +
- + if ntries == 2:
- + self.writeError('Wallet could not be unlocked')
- + return
- +
- + wlt.unlock(securePassphrase=passwd)
- + passwd.destroy()
- +
- + def receiveMessage(self, msg):
- + if isinstance(msg, bool):
- + if msg:
- + print 'Online wallet connected'
- + else:
- + print 'Online wallet disconnected'
- + elif isinstance(msg, SignatureRequest):
- + wlt = self.wallets.get(msg.wallet.uniqueIDB58)
- + if wlt:
- + idx = msg.wallet.lastComputedChainIndex
- + if idx > wlt.lastComputedChainIndex:
- + self.extendWallet(wlt, idx)
- + self.signTxDistProposal(msg)
- + else:
- + self.writeError('Cannot find wallet with id %s' % msg.wallet.uniqueIDB58)
- + elif isinstance(msg, OnlineWalletRequest):
- + response = OnlineWalletResponse()
- + for wlt in self.wallets.itervalues():
- + if not msg.uniqueIDB58 or msg.uniqueIDB58 == wlt.uniqueIDB58:
- + wltmsg = response.wallets.add()
- + wltmsg.uniqueIDB58 = wlt.uniqueIDB58
- + wltmsg.labelName = wlt.labelName
- + wltmsg.labelDescr = wlt.labelDescr
- +
- + if not msg.metadataOnly:
- + copy = wlt.forkOnlineWallet('.temp_wallet')
- + os.remove('.temp_wallet')
- + packer = BinaryPacker()
- + copy.packHeader(packer)
- + data = packer.getBinaryString()
- + for addr160,addrObj in copy.addrMap.iteritems():
- + if not addr160=='ROOT':
- + data += '\x00' + addr160 + addrObj.serialize()
- +
- + for hashVal,comment in copy.commentsMap.iteritems():
- + twoByteLength = int_to_binary(len(comment), widthBytes=2)
- + if len(hashVal)==20:
- + typestr = int_to_binary(WLT_DATATYPE_ADDRCOMMENT)
- + data += typestr + hashVal + twoByteLength + comment
- + elif len(hashVal)==32:
- + typestr = int_to_binary(WLT_DATATYPE_TXCOMMENT)
- + data += typestr + hashVal + twoByteLength + comment
- +
- + wltmsg.packedBytes = zlib.compress(data)
- + print 'Wallet %s size: %d bytes (flat), %d bytes (compressed)' % (wlt.uniqueIDB58, len(data), len(wltmsg.packedBytes))
- +
- + self.conn.write(response)
- + else:
- + print msg.SerializeToString()
- +
- + def signTxDistProposal(self, wlt, request):
- + try:
- + txdp = PyTxDistProposal().unserializeAscii(request.txDP)
- + print 'Received unsigned tx proposal %s' % txdp.uniqueB58
- +
- + found = 0
- + for a160 in txdp.inAddr20Lists:
- + if wlt.hasAddr(a160[0]):
- + found += 1
- +
- + if found == 0:
- + self.writeError('Unable to find any signing keys')
- + return
- + elif found < len(txdp.inAddr20Lists) and request.type == request.FULL:
- + self.writeError('Unable to find all signing keys')
- + return
- +
- + self.unlockWallet(wlt)
- +
- + try:
- + wlt.signTxDistProposal(txdp)
- + if not request.keepWalletUnlocked:
- + wlt.lock()
- +
- + if not txdp.checkTxHasEnoughSignatures() and request.type == request.FULL:
- + self.writeError('Error signing transaction. Most likely this is not the correct wallet')
- + else:
- + msg = SignatureResponse()
- + msg.txDP = txdp.serializeAscii()
- + self.conn.write(msg)
- + print 'Sent signed tx proposal'
- +
- + except WalletLockError:
- + self.writeError('Wallet is somehow still locked')
- + except:
- + self.writeError('Unknown signing error')
- +
- + except IndexError:
- + self.writeError('Invalid transaction distribution proposal')
- +
- +with OfflineSerialServer() as server:
- + server.run()
- + try:
- + while True:
- + time.sleep(1)
- + except KeyboardInterrupt:
- + pass
- Index: armoryengine.py
- IDEA additional info:
- Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
- <+>UTF-8
- ===================================================================
- --- armoryengine.py (revision e1b435821967518f46f5d1722bf6245e5e011e29)
- +++ armoryengine.py (revision )
- @@ -34,7 +34,7 @@
- ################################################################################
- -# Version Numbers
- +# Version Numbers
- BTCARMORY_VERSION = (0, 86, 0, 0) # (Major, Minor, Minor++, even-more-minor)
- PYBTCWALLET_VERSION = (1, 35, 0, 0) # (Major, Minor, Minor++, even-more-minor)
- @@ -58,6 +58,9 @@
- import threading
- from struct import pack, unpack
- from datetime import datetime
- +import serial
- +import serial_pb2 as pb
- +import uuid
- from sys import argv
- @@ -7428,7 +7431,7 @@
- #############################################################################
- - def unpackHeader(self, binUnpacker):
- + def unpackHeader(self, binUnpacker, fileUpdate=True):
- """
- Unpacking the header information from a wallet file. See the help text
- on the base class, PyBtcWallet, for more information on the wallet
- @@ -7489,7 +7492,7 @@
- rawAddrData = binUnpacker.get(BINARY_CHUNK, self.pybtcaddrSize)
- self.addrMap['ROOT'] = PyBtcAddress().unserialize(rawAddrData)
- fixedAddrData = self.addrMap['ROOT'].serialize()
- - if not rawAddrData==fixedAddrData:
- + if fileUpdate and not rawAddrData==fixedAddrData:
- self.walletFileSafeUpdate([ \
- [WLT_UPDATE_MODIFY, self.offsetRootAddr, fixedAddrData]])
- @@ -7555,8 +7558,16 @@
- wltdata = BinaryUnpacker(wltfile.read())
- wltfile.close()
- + TimerStop('readWalletFile')
- +
- + return self.readWalletData(wltdata, doScanNow)
- +
- + #############################################################################
- + def readWalletData(self, wltdata, doScanNow=False, fileUpdate=True):
- + TimerStart('readWalletData')
- +
- self.cppWallet = Cpp.BtcWallet()
- - self.unpackHeader(wltdata)
- + self.unpackHeader(wltdata, fileUpdate=fileUpdate)
- self.lastComputedChainIndex = -UINT32_MAX
- self.lastComputedChainAddr160 = None
- @@ -7569,7 +7580,7 @@
- newAddr.walletByteLoc = byteLocation + 21
- # Fix byte errors in the address data
- fixedAddrData = newAddr.serialize()
- - if not rawData==fixedAddrData:
- + if fileUpdate and not rawData==fixedAddrData:
- self.walletFileSafeUpdate([ \
- [WLT_UPDATE_MODIFY, newAddr.walletByteLoc, fixedAddrData]])
- if newAddr.useEncryption:
- @@ -7608,7 +7619,7 @@
- LOGERROR('Wallets older than version 1.35 no loger supported!')
- return
- - TimerStop('readWalletFile')
- + TimerStop('readWalletData')
- return self
- @@ -11441,14 +11452,112 @@
- f.write('\n\nNote: timings may be incorrect if errors '
- 'were triggered in the timed functions')
- print 'Saved timings to file: %s' % fname
- -
- +
- +class PySerialConnection(threading.Thread):
- + # number of seconds between heartbeats
- + HEARTBEAT_INTERVAL = 60
- + lastHeartbeat = 0
- + lastHeartbeatGuid = ''
- + isConnected = False
- + codeType = {
- + '0': pb.Heartbeat,
- + '1': pb.SignatureRequest,
- + '2': pb.SignatureResponse,
- + '3': pb.OnlineWalletRequest,
- + '4': pb.OnlineWalletResponse,
- + #'5': pb.InputRequest,
- + #'6': pb.InputResponse,
- + '5': pb.Notification,
- + '6': pb.Reset
- + }
- + def __init__(self, device, rate, callback):
- + self.port = serial.Serial(device, rate)
- + self.callback = callback
- + threading.Thread.__init__(self)
- + def __enter__(self):
- + return self
- + def __exit__(self, exc_type, exc_val, exc_tb):
- + self.close()
- + def open(self):
- + self.isOpen = True
- + if not self.port.isOpen():
- + self.port.open()
- + if not self.isAlive():
- + self.start()
- + def close(self):
- + self.isOpen = False
- + if self.port.isOpen():
- + self.port.close()
- + def heartbeat(self, guid=str(uuid.uuid1())):
- + if guid != self.lastHeartbeatGuid:
- + self.lastHeartbeatGuid = guid;
- + beat = pb.Heartbeat()
- + beat.guid = guid
- + self.write(beat)
- + return True
- + return False
- + def setConnected(self, connected):
- + if connected != self.isConnected:
- + self.callback(connected)
- + self.isConnected = connected
- + def write(self, message):
- + with threading.Lock() as lock:
- + code = (code for code, type in self.codeType.items() if isinstance(message, type)).next()
- + serialized = message.SerializeToString()
- + packed_len = pack('!I', len(serialized))
- + self.port.write(code + packed_len + serialized)
- + self.port.flush()
- +
- + def _read(self):
- + code = self._read_n_bytes(1)
- + len_buf = self._read_n_bytes(4)
- + msg_len = unpack('!I', len_buf)[0]
- + msg_buf = self._read_n_bytes(msg_len)
- +
- + msg = self.codeType[code]()
- + msg.ParseFromString(msg_buf)
- + return msg
- +
- + def _read_n_bytes(self, n):
- + buf = ''
- + try:
- + while n > 0:
- + data = self.port.read(n)
- + if data == '':
- + raise RuntimeError('unexpected connection close')
- + buf += data
- + n -= len(data)
- + return buf
- + except OverflowError:
- + print 'Failed to read %d bytes. Waiting: %s' % (n, self.port.read(self.port.inWaiting()))
- + pass
- +
- + def run(self):
- + while self.isOpen:
- + beat = False
- + if self.port.inWaiting():
- + msg = self._read()
- + print 'Received serial message: %s' % msg.SerializeToString()
- + if isinstance(msg, pb.Heartbeat):
- + beat = self.heartbeat(msg.guid)
- + self.setConnected(True)
- + self.lastHeartbeat = time.time()
- +
- + self.callback(msg)
- +
- + if not beat:
- + self.setConnected(time.time() - self.lastHeartbeat < self.HEARTBEAT_INTERVAL)
- + if time.time() - self.lastHeartbeat > self.HEARTBEAT_INTERVAL / 2:
- + self.heartbeat()
- +
- + time.sleep(1)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement