diff options
| -rw-r--r-- | bitcoin-qt.pro | 22 | ||||
| -rwxr-xr-x | contrib/macdeploy/macdeployqtplus | 98 | ||||
| -rw-r--r-- | doc/release-process.txt | 10 | ||||
| -rw-r--r-- | share/certs/BitcoinFoundation_Apple_Cert.pem | 37 | ||||
| -rw-r--r-- | share/certs/BitcoinFoundation_Comodo_Cert.pem | 37 | ||||
| -rw-r--r-- | share/certs/PrivateKeyNotes.md | 46 | ||||
| -rw-r--r-- | src/bitcoinrpc.cpp | 2 | ||||
| -rw-r--r-- | src/init.cpp | 7 | ||||
| -rw-r--r-- | src/main.cpp | 23 | ||||
| -rw-r--r-- | src/noui.cpp | 6 | ||||
| -rw-r--r-- | src/qt/addresstablemodel.cpp | 36 | ||||
| -rw-r--r-- | src/qt/addresstablemodel.h | 23 | ||||
| -rw-r--r-- | src/qt/bitcoin.cpp | 1 | ||||
| -rw-r--r-- | src/qt/editaddressdialog.cpp | 30 | ||||
| -rw-r--r-- | src/qt/editaddressdialog.h | 8 | ||||
| -rw-r--r-- | src/qt/guiutil.cpp | 3 | ||||
| -rw-r--r-- | src/wallet.cpp | 33 |
17 files changed, 316 insertions, 106 deletions
diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 01c4c8632..6a2d7092c 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -27,7 +27,7 @@ contains(RELEASE, 1) { macx:QMAKE_CFLAGS += -mmacosx-version-min=10.5 -arch i386 -isysroot /Developer/SDKs/MacOSX10.5.sdk macx:QMAKE_OBJECTIVE_CFLAGS += -mmacosx-version-min=10.5 -arch i386 -isysroot /Developer/SDKs/MacOSX10.5.sdk - !windows:!macx { + !win32:!macx { # Linux: static link LIBS += -Wl,-Bstatic } @@ -96,7 +96,7 @@ contains(BITCOIN_NEED_QT_PLUGINS, 1) { INCLUDEPATH += src/leveldb/include src/leveldb/helpers LIBS += $$PWD/src/leveldb/libleveldb.a $$PWD/src/leveldb/libmemenv.a -!windows { +!win32 { genleveldb.commands = cd $$PWD/src/leveldb && $(MAKE) libleveldb.a libmemenv.a } else { # make an educated guess about what the ranlib command is called @@ -113,7 +113,7 @@ QMAKE_EXTRA_TARGETS += genleveldb QMAKE_CLEAN += $$PWD/src/leveldb/libleveldb.a; cd $$PWD/src/leveldb ; $(MAKE) clean # regenerate src/build.h -!windows|contains(USE_BUILD_INFO, 1) { +!win32|contains(USE_BUILD_INFO, 1) { genbuild.depends = FORCE genbuild.commands = cd $$PWD; /bin/sh share/genbuild.sh $$OUT_PWD/build/build.h genbuild.target = $$OUT_PWD/build/build.h @@ -325,7 +325,7 @@ OTHER_FILES += \ # platform specific defaults, if not overridden on command line isEmpty(BOOST_LIB_SUFFIX) { macx:BOOST_LIB_SUFFIX = -mt - windows:BOOST_LIB_SUFFIX = -mgw44-mt-s-1_50 + win32:BOOST_LIB_SUFFIX = -mgw44-mt-s-1_50 } isEmpty(BOOST_THREAD_LIB_SUFFIX) { @@ -352,10 +352,10 @@ isEmpty(BOOST_INCLUDE_PATH) { macx:BOOST_INCLUDE_PATH = /opt/local/include } -windows:DEFINES += WIN32 -windows:RC_FILE = src/qt/res/bitcoin-qt.rc +win32:DEFINES += WIN32 +win32:RC_FILE = src/qt/res/bitcoin-qt.rc -windows:!contains(MINGW_THREAD_BUGFIX, 0) { +win32:!contains(MINGW_THREAD_BUGFIX, 0) { # At least qmake's win32-g++-cross profile is missing the -lmingwthrd # thread-safety flag. GCC has -mthreads to enable this, but it doesn't # work with static linking. -lmingwthrd must come BEFORE -lmingw, so @@ -366,7 +366,7 @@ windows:!contains(MINGW_THREAD_BUGFIX, 0) { QMAKE_LIBS_QT_ENTRY = -lmingwthrd $$QMAKE_LIBS_QT_ENTRY } -!windows:!macx { +!win32:!macx { DEFINES += LINUX LIBS += -lrt } @@ -386,12 +386,12 @@ INCLUDEPATH += $$BOOST_INCLUDE_PATH $$BDB_INCLUDE_PATH $$OPENSSL_INCLUDE_PATH $$ LIBS += $$join(BOOST_LIB_PATH,,-L,) $$join(BDB_LIB_PATH,,-L,) $$join(OPENSSL_LIB_PATH,,-L,) $$join(QRENCODE_LIB_PATH,,-L,) LIBS += -lssl -lcrypto -ldb_cxx$$BDB_LIB_SUFFIX # -lgdi32 has to happen after -lcrypto (see #681) -windows:LIBS += -lws2_32 -lshlwapi -lmswsock -lole32 -loleaut32 -luuid -lgdi32 +win32:LIBS += -lws2_32 -lshlwapi -lmswsock -lole32 -loleaut32 -luuid -lgdi32 LIBS += -lboost_system$$BOOST_LIB_SUFFIX -lboost_filesystem$$BOOST_LIB_SUFFIX -lboost_program_options$$BOOST_LIB_SUFFIX -lboost_thread$$BOOST_THREAD_LIB_SUFFIX -windows:LIBS += -lboost_chrono$$BOOST_LIB_SUFFIX +win32:LIBS += -lboost_chrono$$BOOST_LIB_SUFFIX contains(RELEASE, 1) { - !windows:!macx { + !win32:!macx { # Linux: turn dynamic linking back on for c/c++ runtime libraries LIBS += -Wl,-Bdynamic } diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus index cc6053758..11140d3b8 100755 --- a/contrib/macdeploy/macdeployqtplus +++ b/contrib/macdeploy/macdeployqtplus @@ -18,6 +18,7 @@ # import subprocess, sys, re, os, shutil, stat, os.path +from string import Template from time import sleep from argparse import ArgumentParser @@ -429,12 +430,17 @@ ap = ArgumentParser(description="""Improved version of macdeployqt. Outputs a ready-to-deploy app in a folder "dist" and optionally wraps it in a .dmg file. Note, that the "dist" folder will be deleted before deploying on each run. -Optionally, Qt translation files (.qm) and additional resources can be added to the bundle.""") +Optionally, Qt translation files (.qm) and additional resources can be added to the bundle. + +Also optionally signs the .app bundle; set the CODESIGNARGS environment variable to pass arguments +to the codesign tool. +E.g. CODESIGNARGS='--sign "Developer ID Application: ..." --keychain /encrypted/foo.keychain'""") ap.add_argument("app_bundle", nargs=1, metavar="app-bundle", help="application bundle to be deployed") ap.add_argument("-verbose", type=int, nargs=1, default=[1], metavar="<0-3>", help="0 = no output, 1 = error/warning (default), 2 = normal, 3 = debug") ap.add_argument("-no-plugins", dest="plugins", action="store_false", default=True, help="skip plugin deployment") ap.add_argument("-no-strip", dest="strip", action="store_false", default=True, help="don't run 'strip' on the binaries") +ap.add_argument("-sign", dest="sign", action="store_true", default=False, help="sign .app bundle with codesign tool") ap.add_argument("-dmg", nargs="?", const="", metavar="basename", help="create a .dmg disk image; if basename is not specified, a camel-cased version of the app name is used") ap.add_argument("-fancy", nargs=1, metavar="plist", default=[], help="make a fancy looking disk image using the given plist file with instructions; requires -dmg to work") ap.add_argument("-add-qt-tr", nargs=1, metavar="languages", default=[], help="add Qt translation files to the bundle's ressources; the language list must be separated with commas, not with whitespace") @@ -635,6 +641,15 @@ for p in config.add_resources: # ------------------------------------------------ +if config.sign and 'CODESIGNARGS' not in os.environ: + print "You must set the CODESIGNARGS environment variable. Skipping signing." +elif config.sign: + if verbose >= 1: + print "Code-signing app bundle %s"%(target,) + subprocess.check_call("codesign --force %s %s"%(os.environ['CODESIGNARGS'], target), shell=True) + +# ------------------------------------------------ + if config.dmg is not None: def runHDIUtil(verb, image_basename, **kwargs): hdiutil_args = ["hdiutil", verb, image_basename + ".dmg"] @@ -713,31 +728,68 @@ if config.dmg is not None: if fancy.get("applications_symlink", False): os.symlink("/Applications", os.path.join(disk_root, "Applications")) - finder = appscript.app("Finder") - disk = finder.disks[disk_name] - disk.open() - window = disk.container_window - window.current_view.set(appscript.k.icon_view) - window.toolbar_visible.set(False) - window.statusbar_visible.set(False) - if fancy.has_key("window_bounds"): - window.bounds.set(fancy["window_bounds"]) - view_options = window.icon_view_options - view_options.arrangement.set(appscript.k.not_arranged) - if fancy.has_key("icon_size"): - view_options.icon_size.set(fancy["icon_size"]) - if bg_path is not None: - view_options.background_picture.set(disk.files[os.path.basename(bg_path)]) + # The Python appscript package broke with OSX 10.8 and isn't being fixed. + # So we now build up an AppleScript string and use the osascript command + # to make the .dmg file pretty: + appscript = Template( """ + on run argv + tell application "Finder" + tell disk "$disk" + open + set current view of container window to icon view + set toolbar visible of container window to false + set statusbar visible of container window to false + set the bounds of container window to {$window_bounds} + set theViewOptions to the icon view options of container window + set arrangement of theViewOptions to not arranged + set icon size of theViewOptions to $icon_size + $background_commands + $items_positions + close -- close/reopen works around a bug... + open + update without registering applications + delay 5 + eject + end tell + end tell + end run + """) + + itemscript = Template('set position of item "${item}" of container window to {${position}}') + items_positions = [] if fancy.has_key("items_position"): for name, position in fancy["items_position"].iteritems(): - window.items[name].position.set(position) - disk.close() + params = { "item" : name, "position" : ",".join([str(p) for p in position]) } + items_positions.append(itemscript.substitute(params)) + + params = { + "disk" : "Bitcoin-Qt", + "window_bounds" : "300,300,800,620", + "icon_size" : "96", + "background_commands" : "", + "items_positions" : "\n ".join(items_positions) + } + if fancy.has_key("window_bounds"): + params["window.bounds"] = ",".join([str(p) for p in fancy["window_bounds"]]) + if fancy.has_key("icon_size"): + params["icon_size"] = str(fancy["icon_size"]) if bg_path is not None: - subprocess.call(["SetFile", "-a", "V", bg_path]) -# disk.update(registering_applications=False) - sleep(2) - disk.eject() - + # Set background file, then call SetFile to make it invisible. + # (note: making it invisible first makes set background picture fail) + bgscript = Template("""set background picture of theViewOptions to file "$bgpic" + do shell script "SetFile -a V /Volumes/$disk/$bgpic" """) + params["background_commands"] = bgscript.substitute({"bgpic" : os.path.basename(bg_path), "disk" : params["disk"]}) + + s = appscript.substitute(params) + if verbose >= 2: + print("Running AppleScript:") + print(s) + + p = subprocess.Popen(['osascript', '-'], stdin=subprocess.PIPE) + p.communicate(input=s) + if p.returncode: + print("Error running osascript.") + if verbose >= 2: print "+ Finalizing .dmg disk image +" diff --git a/doc/release-process.txt b/doc/release-process.txt index 02f0c47f4..708e32065 100644 --- a/doc/release-process.txt +++ b/doc/release-process.txt @@ -72,12 +72,7 @@ rm -rf bitcoin-${VERSION}-win32 * perform Mac build - See this blog post for how Gavin set up his build environment to build the OSX - release; note that a patched version of macdeployqt is not needed anymore, as - the required functionality and fixes are implemented directly in macdeployqtplus: - http://gavintech.blogspot.com/2011/11/deploying-bitcoin-qt-on-osx.html - Gavin also had trouble with the macports py27-appscript package; he - ended up installing a version that worked with: /usr/bin/easy_install-2.7 appscript + OSX binaries are created by Gavin Andresen on a 32-bit, OSX 10.6 machine. qmake RELEASE=1 USE_UPNP=1 USE_QRCODE=1 bitcoin-qt.pro make @@ -88,6 +83,9 @@ Build output expected: Bitcoin-Qt.dmg +* Code-sign Windows -setup.exe (in a Windows virtual machine) and + OSX Bitcoin-Qt.app (Note: only Gavin has the code-signing keys currently) + * upload builds to SourceForge * create SHA256SUMS for builds, and PGP-sign it diff --git a/share/certs/BitcoinFoundation_Apple_Cert.pem b/share/certs/BitcoinFoundation_Apple_Cert.pem new file mode 100644 index 000000000..beb0d7073 --- /dev/null +++ b/share/certs/BitcoinFoundation_Apple_Cert.pem @@ -0,0 +1,37 @@ +Bag Attributes + friendlyName: Developer ID Application: BITCOIN FOUNDATION, INC., THE + localKeyID: 6B 9C 6C A8 A5 73 70 70 E2 57 A3 49 D8 62 FB 97 C7 A5 5D 5E +subject=/UID=PBV4GLS9J4/CN=Developer ID Application: BITCOIN FOUNDATION, INC., THE/OU=PBV4GLS9J4/O=BITCOIN FOUNDATION, INC., THE/C=US +issuer=/CN=Developer ID Certification Authority/OU=Apple Certification Authority/O=Apple Inc./C=US +-----BEGIN CERTIFICATE----- +MIIFhzCCBG+gAwIBAgIIJ0r1rumyfZAwDQYJKoZIhvcNAQELBQAweTEtMCsGA1UE +AwwkRGV2ZWxvcGVyIElEIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSYwJAYDVQQL +DB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUg +SW5jLjELMAkGA1UEBhMCVVMwHhcNMTMwMTEwMjIzOTAxWhcNMTgwMTExMjIzOTAx +WjCBqDEaMBgGCgmSJomT8ixkAQEMClBCVjRHTFM5SjQxQDA+BgNVBAMMN0RldmVs +b3BlciBJRCBBcHBsaWNhdGlvbjogQklUQ09JTiBGT1VOREFUSU9OLCBJTkMuLCBU +SEUxEzARBgNVBAsMClBCVjRHTFM5SjQxJjAkBgNVBAoMHUJJVENPSU4gRk9VTkRB +VElPTiwgSU5DLiwgVEhFMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBALTd5zURuZVoJviusr119aktXksenb9IN9vq6kBbq38vxEk7 +9wkKMES2XfBRh0HxcEizGzhMNy5OCXuTLMaNMihYdfwYSoBoR2foEU+6kjPUnyJ4 +dQBFLJZJr5/QeQmALmYHEgZ6lwXFD2lU8t92340zeJ4y5LZw5pcEHtH9IummYDut +OGCkCGXDcjL+5nHhNScJiXHhswM+62o6XXsQiP6EWbM1CsgrGTNLtaa0U/UvVDwE +79YKklSC5Bog2LD0jBcTuveI66mFzqu++L9X9u+ZArtebwCl7BPNQ+uboYy5uV2d +zf8lpNNZLfXCFjoLe9bLICKfZ7ub9V5aC8+GhckCAwEAAaOCAeEwggHdMD4GCCsG +AQUFBwEBBDIwMDAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuYXBwbGUuY29tL29j +c3AtZGV2aWQwMTAdBgNVHQ4EFgQUa5xsqKVzcHDiV6NJ2GL7l8elXV4wDAYDVR0T +AQH/BAIwADAfBgNVHSMEGDAWgBRXF+2iz9x8mKEQ4Py+hy0s8uMXVDCCAQ4GA1Ud +IASCAQUwggEBMIH+BgkqhkiG92NkBQEwgfAwKAYIKwYBBQUHAgEWHGh0dHA6Ly93 +d3cuYXBwbGUuY29tL2FwcGxlY2EwgcMGCCsGAQUFBwICMIG2DIGzUmVsaWFuY2Ug +b24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2NlcHRh +bmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNv +bmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRpZmlj +YXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wDgYDVR0PAQH/BAQDAgeAMBYGA1Ud +JQEB/wQMMAoGCCsGAQUFBwMDMBMGCiqGSIb3Y2QGAQ0BAf8EAgUAMA0GCSqGSIb3 +DQEBCwUAA4IBAQAfJ0BjID/1dS2aEeVyhAzPzCBjG8vm0gDf+/qfwRn3+yWeL9vS +nMdbilwM48IyQWTagjGGcojbsAd/vE4N7NhQyHInoCllNoeor1I5xx+blTaGRBK+ +dDhJbbdlGCjsLnH/BczGZi5fyEJds9lUIrp1hJidRcUKO76qb/9gc6qNZpl1vH5k +lDUuJYt7YhAs+L6rTXDyqcK9maeQr0gaOPsRRAQLLwiQCorPeMTUNsbVMdMwZYJs +R+PxiAnk+nyi7rfiFvPoASAYUuI6OzYL/Fa6QU4/gYyPgic944QYVkaQBnc0vEP1 +nXq6LGKwgVGcqJnkr/E2kui5gJoV5C3qll3e +-----END CERTIFICATE----- diff --git a/share/certs/BitcoinFoundation_Comodo_Cert.pem b/share/certs/BitcoinFoundation_Comodo_Cert.pem new file mode 100644 index 000000000..dc752d455 --- /dev/null +++ b/share/certs/BitcoinFoundation_Comodo_Cert.pem @@ -0,0 +1,37 @@ +Bag Attributes + friendlyName: The Bitcoin Foundation, Inc.'s COMODO CA Limited ID + localKeyID: 8C 94 64 E3 B5 B0 41 89 5B 89 B0 57 CC 74 B9 44 E5 B2 92 66 +subject=/C=US/postalCode=98104-1444/ST=WA/L=Seattle/street=Suite 300/street=71 Columbia St/O=The Bitcoin Foundation, Inc./CN=The Bitcoin Foundation, Inc. +issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO Code Signing CA 2 +-----BEGIN CERTIFICATE----- +MIIFeDCCBGCgAwIBAgIRAJVYMd+waOER7lUqtiz3M2IwDQYJKoZIhvcNAQEFBQAw +ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxITAfBgNV +BAMTGENPTU9ETyBDb2RlIFNpZ25pbmcgQ0EgMjAeFw0xMzAxMTYwMDAwMDBaFw0x +NDAxMTYyMzU5NTlaMIG8MQswCQYDVQQGEwJVUzETMBEGA1UEEQwKOTgxMDQtMTQ0 +NDELMAkGA1UECAwCV0ExEDAOBgNVBAcMB1NlYXR0bGUxEjAQBgNVBAkMCVN1aXRl +IDMwMDEXMBUGA1UECQwONzEgQ29sdW1iaWEgU3QxJTAjBgNVBAoMHFRoZSBCaXRj +b2luIEZvdW5kYXRpb24sIEluYy4xJTAjBgNVBAMMHFRoZSBCaXRjb2luIEZvdW5k +YXRpb24sIEluYy4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQChUwLD +u/hu5aFZ/n11B27awONaaDrmHm0pamiWHb01yL4JmTBtaLCrSftF8RhCscQ8jpI0 +UG1Cchmay0e3zH5o5XRs0H9C3x+SM5ozms0TWDmAYiB8aQEghsGovDk0D2nyTQeK +Q0xqyCh0m8ZPOnMnYrakHEmF6WvhLdJvI6Od4KIwbKxgN17cPFIfLVsZ7GrzmmbU +Gdi4wSQCHy5rxzvBxho8Qq/SfBl93uOMUrqOHjOUAPhNuTJG3t/MdhU8Zp24s29M +abHtYkT9W86hMjIiI8RTAR+WHKVglx9SB0cjDabXN8SZ3gME0+H++LyzlySHT8sI +ykepojZ7UBRgp9w3AgMBAAGjggGzMIIBrzAfBgNVHSMEGDAWgBQexbEsfYfaAmh8 +JbwMB4Q/ts/e8TAdBgNVHQ4EFgQUfPf+ZyDWl/4LH0Y5BuJTelkRd/EwDgYDVR0P +AQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEQYJ +YIZIAYb4QgEBBAQDAgQQMEYGA1UdIAQ/MD0wOwYMKwYBBAGyMQECAQMCMCswKQYI +KwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5uZXQvQ1BTMEEGA1UdHwQ6 +MDgwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9ET0NvZGVTaWdu +aW5nQ0EyLmNybDByBggrBgEFBQcBAQRmMGQwPAYIKwYBBQUHMAKGMGh0dHA6Ly9j +cnQuY29tb2RvY2EuY29tL0NPTU9ET0NvZGVTaWduaW5nQ0EyLmNydDAkBggrBgEF +BQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMCgGA1UdEQQhMB+BHWxpbmRz +YXlAYml0Y29pbmZvdW5kYXRpb24ub3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAqibjo +D4HG5XSIIMCmYE5RgQBSEAJfI+EZERk1G9F83ZUWr0yNRZCw4O+RaM7xQhvJhEoD +G2kpk/q2bNOc71/VyZ6SrE1JRVUON41/Flhz4M6cP0BclTicXvh+efVwqZhIz+ws +UxF2hvC/1Xx6rqI7NYAlOYXk2MSUq3HREo+gWUPKM8em4MZZV/7XCH4QbsfxOl1J +xS6EOQmV8hfUN4KRXI5WfGUmedBxq7dM0RSJOSQl8fq2f+JjRLfjQwQucy7LDY+y +pRTsL2TdQV/DuDuI3s0NHRGznQNddoX5jqpXhSQFAAdgrhN1gGkWaaTPzr9IF2TG +qgr6PEp9tIYC+MbM +-----END CERTIFICATE----- diff --git a/share/certs/PrivateKeyNotes.md b/share/certs/PrivateKeyNotes.md new file mode 100644 index 000000000..da299d168 --- /dev/null +++ b/share/certs/PrivateKeyNotes.md @@ -0,0 +1,46 @@ +Code-signing private key notes +== + +The private keys for these certificates were generated on Gavin's main work machine, +following the certificate authoritys' recommendations for generating certificate +signing requests. + +For OSX, the private key was generated by Keychain.app on Gavin's main work machine. +The key and certificate is in a separate, passphrase-protected keychain file that is +unlocked to sign the Bitcoin-Qt.app bundle. + +For Windows, the private key was generated by Firefox running on Gavin's main work machine. +The key and certificate were exported into a separate, passphrase-protected PKCS#12 file, and +then deleted from Firefox's keystore. The exported file is used to sign the Windows setup.exe. + +Threat analysis +-- + +Gavin is a single point of failure. He could be coerced to divulge the secret signing keys, +allowing somebody to distribute a Bitcoin-Qt.app or bitcoin-qt-setup.exe with a valid +signature but containing a malicious binary. + +Or the machine Gavin uses to sign the binaries could be compromised, either remotely or +by breaking in to his office, allowing the attacker to get the private key files and then +install a keylogger to get the passphrase that protects them. + +Threat Mitigation +-- + +"Air gapping" the machine used to do the signing will not work, because the signing +process needs to access a timestamp server over the network. And it would not +prevent the "rubber hose cryptography" threat (coercing Gavin to sign a bad binary +or divulge the private keys). + +Windows binaries are reproducibly 'gitian-built', and the setup.exe file created +by the NSIS installer system is a 7zip archive, so you could check to make sure +that the bitcoin-qt.exe file inside the installer had not been tampered with. +However, an attacker could modify the installer's code, so when the setup.exe +was run it compromised users' systems. A volunteer to write an auditing tool +that checks the setup.exe for tampering, and checks the files in it against +the list of gitian signatures, is needed. + +The long-term solution is something like the 'gitian downloader' system, which +uses signatures from multiple developers to determine whether or not a binary +should be trusted. However, that just pushes the problem to "how will +non-technical users securely get the gitian downloader code to start?" diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 41850d8bb..a1e39d750 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -176,7 +176,7 @@ Value help(const Array& params, bool fHelp) Value stop(const Array& params, bool fHelp) { - // Accept the deprecated and ignored 'detach´ boolean argument + // Accept the deprecated and ignored 'detach' boolean argument if (fHelp || params.size() > 1) throw runtime_error( "stop\n" diff --git a/src/init.cpp b/src/init.cpp index 4f319e8cb..1d49a7bed 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -789,7 +789,7 @@ bool AppInit2() nCoinCacheSize = nTotalCache / 300; // coins in memory require around 300 bytes uiInterface.InitMessage(_("Loading block index...")); - printf("Loading block index...\n"); + nStart = GetTimeMillis(); pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex); pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex); @@ -848,7 +848,7 @@ bool AppInit2() // ********************************************************* Step 8: load wallet uiInterface.InitMessage(_("Loading wallet...")); - printf("Loading wallet...\n"); + nStart = GetTimeMillis(); bool fFirstRun = true; pwalletMain = new CWallet("wallet.dat"); @@ -946,7 +946,7 @@ bool AppInit2() // ********************************************************* Step 10: load peers uiInterface.InitMessage(_("Loading addresses...")); - printf("Loading addresses...\n"); + nStart = GetTimeMillis(); { @@ -981,7 +981,6 @@ bool AppInit2() // ********************************************************* Step 12: finished uiInterface.InitMessage(_("Done loading")); - printf("Done loading\n"); if (!strErrors.str().empty()) return InitError(strErrors.str()); diff --git a/src/main.cpp b/src/main.cpp index 4febf332d..adc4ac0d2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1592,6 +1592,14 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust // verify that the view's current state corresponds to the previous block assert(pindex->pprev == view.GetBestBlock()); + // Special case for the genesis block, skipping connection of its transactions + // (its coinbase is unspendable) + if (GetHash() == hashGenesisBlock) { + view.SetBestBlock(pindex); + pindexGenesisBlock = pindex; + return true; + } + bool fScriptChecks = pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(); // Do not allow blocks that contain transactions which 'overwrite' older transactions, @@ -1727,21 +1735,6 @@ bool SetBestChain(CBlockIndex* pindexNew) // Only when all have succeeded, we push it to pcoinsTip. CCoinsViewCache view(*pcoinsTip, true); - // special case for attaching the genesis block - // note that no ConnectBlock is called, so its coinbase output is non-spendable - if (pindexGenesisBlock == NULL && pindexNew->GetBlockHash() == hashGenesisBlock) - { - view.SetBestBlock(pindexNew); - if (!view.Flush()) - return false; - pindexGenesisBlock = pindexNew; - pindexBest = pindexNew; - hashBestChain = pindexNew->GetBlockHash(); - nBestHeight = pindexBest->nHeight; - bnBestChainWork = pindexNew->bnChainWork; - return true; - } - // Find the fork (typically, there is none) CBlockIndex* pfork = view.GetBestBlock(); CBlockIndex* plonger = pindexNew; diff --git a/src/noui.cpp b/src/noui.cpp index 96a8de4ee..302d05929 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -37,9 +37,15 @@ static bool noui_ThreadSafeAskFee(int64 /*nFeeRequired*/) return true; } +static void noui_InitMessage(const std::string &message) +{ + printf("init message: %s\n", message.c_str()); +} + void noui_connect() { // Connect bitcoind signal handlers uiInterface.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox); uiInterface.ThreadSafeAskFee.connect(noui_ThreadSafeAskFee); + uiInterface.InitMessage.connect(noui_InitMessage); } diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index e65d3915e..03b09cdce 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -69,6 +69,8 @@ public: QString::fromStdString(address.ToString()))); } } + // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order + qSort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan()); } void updateEntry(const QString &address, const QString &label, bool isMine, int status) @@ -208,7 +210,7 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const return QVariant(); } -bool AddressTableModel::setData(const QModelIndex & index, const QVariant & value, int role) +bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role) { if(!index.isValid()) return false; @@ -221,18 +223,36 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu switch(index.column()) { case Label: + // Do nothing, if old label == new label + if(rec->label == value.toString()) + { + editStatus = NO_CHANGES; + return false; + } wallet->SetAddressBookName(CBitcoinAddress(rec->address.toStdString()).Get(), value.toString().toStdString()); - rec->label = value.toString(); break; case Address: + // Do nothing, if old address == new address + if(CBitcoinAddress(rec->address.toStdString()) == CBitcoinAddress(value.toString().toStdString())) + { + editStatus = NO_CHANGES; + return false; + } // Refuse to set invalid address, set error status and return false - if(!walletModel->validateAddress(value.toString())) + else if(!walletModel->validateAddress(value.toString())) { editStatus = INVALID_ADDRESS; return false; } + // Check for duplicate addresses to prevent accidental deletion of addresses, if you try + // to paste an existing address over another address (with a different label) + else if(wallet->mapAddressBook.count(CBitcoinAddress(value.toString().toStdString()).Get())) + { + editStatus = DUPLICATE_ADDRESS; + return false; + } // Double-check that we're not overwriting a receiving address - if(rec->type == AddressTableEntry::Sending) + else if(rec->type == AddressTableEntry::Sending) { { LOCK(wallet->cs_wallet); @@ -244,7 +264,6 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu } break; } - return true; } return false; @@ -262,7 +281,7 @@ QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, return QVariant(); } -Qt::ItemFlags AddressTableModel::flags(const QModelIndex & index) const +Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const { if(!index.isValid()) return 0; @@ -279,7 +298,7 @@ Qt::ItemFlags AddressTableModel::flags(const QModelIndex & index) const return retval; } -QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & parent) const +QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent); AddressTableEntry *data = priv->index(row); @@ -345,6 +364,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con { return QString(); } + // Add entry { LOCK(wallet->cs_wallet); @@ -353,7 +373,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con return QString::fromStdString(strAddress); } -bool AddressTableModel::removeRows(int row, int count, const QModelIndex & parent) +bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent) { Q_UNUSED(parent); AddressTableEntry *rec = priv->index(row); diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index 42974e3e1..ae3e3b2f0 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -29,26 +29,27 @@ public: /** Return status of edit/insert operation */ enum EditStatus { - OK, - INVALID_ADDRESS, /**< Unparseable address */ - DUPLICATE_ADDRESS, /**< Address already in address book */ - WALLET_UNLOCK_FAILURE, /**< Wallet could not be unlocked to create new receiving address */ - KEY_GENERATION_FAILURE /**< Generating a new public key for a receiving address failed */ + OK, /**< Everything ok */ + NO_CHANGES, /**< No changes were made during edit operation */ + INVALID_ADDRESS, /**< Unparseable address */ + DUPLICATE_ADDRESS, /**< Address already in address book */ + WALLET_UNLOCK_FAILURE, /**< Wallet could not be unlocked to create new receiving address */ + KEY_GENERATION_FAILURE /**< Generating a new public key for a receiving address failed */ }; - static const QString Send; /**< Specifies send address */ - static const QString Receive; /**< Specifies receive address */ + static const QString Send; /**< Specifies send address */ + static const QString Receive; /**< Specifies receive address */ /** @name Methods overridden from QAbstractTableModel @{*/ int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; - bool setData(const QModelIndex & index, const QVariant & value, int role); + bool setData(const QModelIndex &index, const QVariant &value, int role); QVariant headerData(int section, Qt::Orientation orientation, int role) const; - QModelIndex index(int row, int column, const QModelIndex & parent) const; - bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex()); - Qt::ItemFlags flags(const QModelIndex & index) const; + QModelIndex index(int row, int column, const QModelIndex &parent) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + Qt::ItemFlags flags(const QModelIndex &index) const; /*@}*/ /* Add an address to the model. diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index c3701ced7..e5526a6c0 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -86,6 +86,7 @@ static void InitMessage(const std::string &message) splashref->showMessage(QString::fromStdString(message), Qt::AlignBottom|Qt::AlignHCenter, QColor(255,255,200)); QApplication::instance()->processEvents(); } + printf("init message: %s\n", message.c_str()); } static void QueueShutdown() diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index 0d88aa47c..5cfcb34b9 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -25,7 +25,7 @@ EditAddressDialog::EditAddressDialog(Mode mode, QWidget *parent) : break; case EditReceivingAddress: setWindowTitle(tr("Edit receiving address")); - ui->addressEdit->setDisabled(true); + ui->addressEdit->setEnabled(false); break; case EditSendingAddress: setWindowTitle(tr("Edit sending address")); @@ -44,6 +44,9 @@ EditAddressDialog::~EditAddressDialog() void EditAddressDialog::setModel(AddressTableModel *model) { this->model = model; + if(!model) + return; + mapper->setModel(model); mapper->addMapping(ui->labelEdit, AddressTableModel::Label); mapper->addMapping(ui->addressEdit, AddressTableModel::Address); @@ -58,6 +61,7 @@ bool EditAddressDialog::saveCurrentRow() { if(!model) return false; + switch(mode) { case NewReceivingAddress: @@ -82,35 +86,39 @@ void EditAddressDialog::accept() { if(!model) return; + if(!saveCurrentRow()) { switch(model->getEditStatus()) { - case AddressTableModel::DUPLICATE_ADDRESS: - QMessageBox::warning(this, windowTitle(), - tr("The entered address \"%1\" is already in the address book.").arg(ui->addressEdit->text()), - QMessageBox::Ok, QMessageBox::Ok); + case AddressTableModel::OK: + // Failed with unknown reason. Just reject. + break; + case AddressTableModel::NO_CHANGES: + // No changes were made during edit operation. Just reject. break; case AddressTableModel::INVALID_ADDRESS: QMessageBox::warning(this, windowTitle(), tr("The entered address \"%1\" is not a valid Bitcoin address.").arg(ui->addressEdit->text()), QMessageBox::Ok, QMessageBox::Ok); - return; + break; + case AddressTableModel::DUPLICATE_ADDRESS: + QMessageBox::warning(this, windowTitle(), + tr("The entered address \"%1\" is already in the address book.").arg(ui->addressEdit->text()), + QMessageBox::Ok, QMessageBox::Ok); + break; case AddressTableModel::WALLET_UNLOCK_FAILURE: QMessageBox::critical(this, windowTitle(), tr("Could not unlock wallet."), QMessageBox::Ok, QMessageBox::Ok); - return; + break; case AddressTableModel::KEY_GENERATION_FAILURE: QMessageBox::critical(this, windowTitle(), tr("New key generation failed."), QMessageBox::Ok, QMessageBox::Ok); - return; - case AddressTableModel::OK: - // Failed with unknown reason. Just reject. break; - } + } return; } QDialog::accept(); diff --git a/src/qt/editaddressdialog.h b/src/qt/editaddressdialog.h index 7ec053f13..0e4183bd5 100644 --- a/src/qt/editaddressdialog.h +++ b/src/qt/editaddressdialog.h @@ -27,15 +27,17 @@ public: }; explicit EditAddressDialog(Mode mode, QWidget *parent = 0); - ~EditAddressDialog(); + ~EditAddressDialog(); void setModel(AddressTableModel *model); void loadRow(int row); - void accept(); - QString getAddress() const; void setAddress(const QString &address); + +public slots: + void accept(); + private: bool saveCurrentRow(); diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index ff70ca24a..d4e73adf9 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -77,7 +77,8 @@ void setupAmountWidget(QLineEdit *widget, QWidget *parent) bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out) { - if(uri.scheme() != QString("bitcoin")) + // return if URI is not valid or is no bitcoin URI + if(!uri.isValid() || uri.scheme() != QString("bitcoin")) return false; SendCoinsRecipient rv; diff --git a/src/wallet.cpp b/src/wallet.cpp index f49bfb5f8..8b2f03212 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1636,29 +1636,38 @@ set< set<CTxDestination> > CWallet::GetAddressGroupings() { CWalletTx *pcoin = &walletEntry.second; - if (pcoin->vin.size() > 0 && IsMine(pcoin->vin[0])) + if (pcoin->vin.size() > 0) { + bool any_mine = false; // group all input addresses with each other BOOST_FOREACH(CTxIn txin, pcoin->vin) { CTxDestination address; + if(!IsMine(txin)) /* If this input isn't mine, ignore it */ + continue; if(!ExtractDestination(mapWallet[txin.prevout.hash].vout[txin.prevout.n].scriptPubKey, address)) continue; grouping.insert(address); + any_mine = true; } // group change with input addresses - BOOST_FOREACH(CTxOut txout, pcoin->vout) - if (IsChange(txout)) - { - CWalletTx tx = mapWallet[pcoin->vin[0].prevout.hash]; - CTxDestination txoutAddr; - if(!ExtractDestination(txout.scriptPubKey, txoutAddr)) - continue; - grouping.insert(txoutAddr); - } - groupings.insert(grouping); - grouping.clear(); + if (any_mine) + { + BOOST_FOREACH(CTxOut txout, pcoin->vout) + if (IsChange(txout)) + { + CTxDestination txoutAddr; + if(!ExtractDestination(txout.scriptPubKey, txoutAddr)) + continue; + grouping.insert(txoutAddr); + } + } + if (grouping.size() > 0) + { + groupings.insert(grouping); + grouping.clear(); + } } // group lone addrs by themselves |