aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bitcoin-qt.pro22
-rwxr-xr-xcontrib/macdeploy/macdeployqtplus98
-rw-r--r--doc/release-process.txt10
-rw-r--r--share/certs/BitcoinFoundation_Apple_Cert.pem37
-rw-r--r--share/certs/BitcoinFoundation_Comodo_Cert.pem37
-rw-r--r--share/certs/PrivateKeyNotes.md46
-rw-r--r--src/bitcoinrpc.cpp2
-rw-r--r--src/init.cpp7
-rw-r--r--src/main.cpp23
-rw-r--r--src/noui.cpp6
-rw-r--r--src/qt/addresstablemodel.cpp36
-rw-r--r--src/qt/addresstablemodel.h23
-rw-r--r--src/qt/bitcoin.cpp1
-rw-r--r--src/qt/editaddressdialog.cpp30
-rw-r--r--src/qt/editaddressdialog.h8
-rw-r--r--src/qt/guiutil.cpp3
-rw-r--r--src/wallet.cpp33
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