diff options
257 files changed, 9495 insertions, 4424 deletions
@@ -1,4 +1,4 @@ -Copyright (c) 2009-2015 Bitcoin Developers +Copyright (c) 2009-2015 The Bitcoin Core developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile.am b/Makefile.am index 2cb933d5bb..2f478e4b41 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,10 +15,11 @@ BITCOIN_CLI_BIN=$(top_builddir)/src/bitcoin-cli$(EXEEXT) BITCOIN_WIN_INSTALLER=$(PACKAGE)-$(PACKAGE_VERSION)-win$(WINDOWS_BITS)-setup$(EXEEXT) OSX_APP=Bitcoin-Qt.app -OSX_DMG=Bitcoin-Qt.dmg +OSX_DMG=Bitcoin-Core.dmg OSX_BACKGROUND_IMAGE=background.tiff OSX_DEPLOY_SCRIPT=$(top_srcdir)/contrib/macdeploy/macdeployqtplus OSX_FANCY_PLIST=$(top_srcdir)/contrib/macdeploy/fancy.plist +OSX_BASE_LPROJ_DIR=$(top_srcdir)/contrib/macdeploy/Base.lproj/InfoPlist.strings OSX_INSTALLER_ICONS=$(top_srcdir)/src/qt/res/icons/bitcoin.icns OSX_PLIST=$(top_srcdir)/share/qt/Info.plist #not installed OSX_QT_TRANSLATIONS = da,de,es,hu,ru,uk,zh_CN,zh_TW @@ -30,7 +31,7 @@ WINDOWS_PACKAGING = $(top_srcdir)/share/pixmaps/bitcoin.ico \ $(top_srcdir)/share/pixmaps/nsis-wizard.bmp \ $(top_srcdir)/doc/README_windows.txt -OSX_PACKAGING = $(OSX_DEPLOY_SCRIPT) $(OSX_FANCY_PLIST) $(OSX_INSTALLER_ICONS) \ +OSX_PACKAGING = $(OSX_DEPLOY_SCRIPT) $(OSX_FANCY_PLIST) $(OSX_INSTALLER_ICONS) $(OSX_BASE_LPROJ_DIR) \ $(top_srcdir)/contrib/macdeploy/$(OSX_BACKGROUND_IMAGE) \ $(top_srcdir)/contrib/macdeploy/DS_Store \ $(top_srcdir)/contrib/macdeploy/detached-sig-apply.sh \ @@ -72,7 +73,7 @@ $(OSX_APP)/Contents/PkgInfo: $(OSX_APP)/Contents/Resources/empty.lproj: $(MKDIR_P) $(@D) - @touch $@ + @touch $@ $(OSX_APP)/Contents/Info.plist: $(OSX_PLIST) $(MKDIR_P) $(@D) @@ -86,9 +87,13 @@ $(OSX_APP)/Contents/MacOS/Bitcoin-Qt: $(BITCOIN_QT_BIN) $(MKDIR_P) $(@D) STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $< $@ +$(OSX_APP)/Contents/Resources/Base.lproj/InfoPlist.strings: $(OSX_BASE_LPROJ_DIR) + $(MKDIR_P) $(@D) + $(INSTALL_DATA) $< $@ + OSX_APP_BUILT=$(OSX_APP)/Contents/PkgInfo $(OSX_APP)/Contents/Resources/empty.lproj \ $(OSX_APP)/Contents/Resources/bitcoin.icns $(OSX_APP)/Contents/Info.plist \ - $(OSX_APP)/Contents/MacOS/Bitcoin-Qt + $(OSX_APP)/Contents/MacOS/Bitcoin-Qt $(OSX_APP)/Contents/Resources/Base.lproj/InfoPlist.strings if BUILD_DARWIN $(OSX_DMG): $(OSX_APP_BUILT) $(OSX_PACKAGING) @@ -106,7 +111,7 @@ $(APP_DIST_DIR)/Applications: $(APP_DIST_EXTRAS): $(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/Bitcoin-Qt $(OSX_DMG): $(APP_DIST_EXTRAS) - $(GENISOIMAGE) -no-cache-inodes -D -l -probe -V "Bitcoin-Qt" -no-pad -r -apple -o $@ dist + $(GENISOIMAGE) -no-cache-inodes -D -l -probe -V "Bitcoin-Core" -no-pad -r -apple -o $@ dist $(APP_DIST_DIR)/.background/$(OSX_BACKGROUND_IMAGE): contrib/macdeploy/$(OSX_BACKGROUND_IMAGE) $(MKDIR_P) $(@D) diff --git a/configure.ac b/configure.ac index 7c0a9f4a94..61b6a314af 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 0) -define(_CLIENT_VERSION_MINOR, 10) +define(_CLIENT_VERSION_MINOR, 11) define(_CLIENT_VERSION_REVISION, 99) define(_CLIENT_VERSION_BUILD, 0) define(_CLIENT_VERSION_IS_RELEASE, false) @@ -438,7 +438,7 @@ if test x$TARGET_OS = xdarwin; then AX_CHECK_LINK_FLAG([[-Wl,-dead_strip]], [LDFLAGS="$LDFLAGS -Wl,-dead_strip"]) fi -AC_CHECK_HEADERS([endian.h byteswap.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h sys/prctl.h]) +AC_CHECK_HEADERS([endian.h sys/endian.h byteswap.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h sys/prctl.h]) AC_SEARCH_LIBS([getaddrinfo_a], [anl], [AC_DEFINE(HAVE_GETADDRINFO_A, 1, [Define this symbol if you have getaddrinfo_a])]) AC_SEARCH_LIBS([inet_pton], [nsl resolv], [AC_DEFINE(HAVE_INET_PTON, 1, [Define this symbol if you have inet_pton])]) @@ -447,6 +447,8 @@ AC_CHECK_DECLS([strnlen]) AC_CHECK_DECLS([le16toh, le32toh, le64toh, htole16, htole32, htole64, be16toh, be32toh, be64toh, htobe16, htobe32, htobe64],,, [#if HAVE_ENDIAN_H #include <endian.h> + #elif HAVE_SYS_ENDIAN_H + #include <sys/endian.h> #endif]) AC_CHECK_DECLS([bswap_16, bswap_32, bswap_64],,, @@ -590,17 +592,15 @@ fi if test x$use_boost = xyes; then -BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB $BOOST_PROGRAM_OPTIONS_LIB $BOOST_THREAD_LIB" +BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB $BOOST_PROGRAM_OPTIONS_LIB $BOOST_THREAD_LIB $BOOST_CHRONO_LIB" dnl Boost >= 1.50 uses sleep_for rather than the now-deprecated sleep, however dnl it was broken from 1.50 to 1.52 when backed by nanosleep. Use sleep_for if dnl a working version is available, else fall back to sleep. sleep was removed dnl after 1.56. dnl If neither is available, abort. -dnl If sleep_for is used, boost_chrono becomes a requirement. -if test x$ax_cv_boost_chrono = xyes; then TEMP_LIBS="$LIBS" -LIBS="$BOOST_LIBS $BOOST_CHRONO_LIB $LIBS" +LIBS="$BOOST_LIBS $LIBS" TEMP_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @@ -613,12 +613,11 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ choke me #endif ]])], - [boost_sleep=yes; BOOST_LIBS="$BOOST_LIBS $BOOST_CHRONO_LIB"; + [boost_sleep=yes; AC_DEFINE(HAVE_WORKING_BOOST_SLEEP_FOR, 1, [Define this symbol if boost sleep_for works])], [boost_sleep=no]) LIBS="$TEMP_LIBS" CPPFLAGS="$TEMP_CPPFLAGS" -fi if test x$boost_sleep != xyes; then TEMP_LIBS="$LIBS" @@ -680,6 +679,14 @@ else fi fi +AC_CHECK_LIB([crypto],[RAND_egd],[],[ + AC_ARG_WITH([libressl], + [AS_HELP_STRING([--with-libressl],[Build with system LibreSSL (default is no; DANGEROUS; NOT SUPPORTED)])], + [AC_MSG_WARN([Detected LibreSSL: This is NOT supported, and may break consensus compatibility!])], + [AC_MSG_ERROR([Detected LibreSSL: This is NOT supported, and may break consensus compatibility!])] + ) +]) + CFLAGS_TEMP="$CFLAGS" LIBS_TEMP="$LIBS" CFLAGS="$CFLAGS $SSL_CFLAGS $CRYPTO_CFLAGS" @@ -888,7 +895,7 @@ PKGCONFIG_LIBDIR_TEMP="$PKG_CONFIG_LIBDIR" unset PKG_CONFIG_LIBDIR PKG_CONFIG_LIBDIR="$PKGCONFIG_LIBDIR_TEMP" -ac_configure_args="${ac_configure_args} --disable-shared --with-pic" +ac_configure_args="${ac_configure_args} --disable-shared --with-pic --with-bignum=no" AC_CONFIG_SUBDIRS([src/secp256k1]) AC_OUTPUT diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index dde4af3491..440d95a355 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "bitcoin-linux-0.10" +name: "bitcoin-linux-0.12" enable_cache: true suites: - "precise" @@ -16,7 +16,7 @@ packages: - "bsdmainutils" - "binutils-gold" - "libstdc++6-4.6-pic" -reference_datetime: "2013-06-01 00:00:00" +reference_datetime: "2015-06-01 00:00:00" remotes: - "url": "https://github.com/bitcoin/bitcoin.git" "dir": "bitcoin" diff --git a/contrib/gitian-descriptors/gitian-osx-signer.yml b/contrib/gitian-descriptors/gitian-osx-signer.yml index 1792f9de3f..c0c0b0c98b 100644 --- a/contrib/gitian-descriptors/gitian-osx-signer.yml +++ b/contrib/gitian-descriptors/gitian-osx-signer.yml @@ -7,7 +7,7 @@ architectures: packages: - "libc6:i386" - "faketime" -reference_datetime: "2013-06-01 00:00:00" +reference_datetime: "2015-06-01 00:00:00" remotes: [] files: - "bitcoin-osx-unsigned.tar.gz" @@ -33,5 +33,5 @@ script: | tar -xf ${UNSIGNED} ./detached-sig-apply.sh ${UNSIGNED} signature.tar.gz - ${WRAP_DIR}/genisoimage -no-cache-inodes -D -l -probe -V "Bitcoin-Qt" -no-pad -r -apple -o uncompressed.dmg signed-app + ${WRAP_DIR}/genisoimage -no-cache-inodes -D -l -probe -V "Bitcoin-Core" -no-pad -r -apple -o uncompressed.dmg signed-app ${WRAP_DIR}/dmg dmg uncompressed.dmg ${OUTDIR}/${SIGNED} diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml index b401482c70..bad9a0e768 100644 --- a/contrib/gitian-descriptors/gitian-osx.yml +++ b/contrib/gitian-descriptors/gitian-osx.yml @@ -1,5 +1,5 @@ --- -name: "bitcoin-osx-0.10" +name: "bitcoin-osx-0.12" enable_cache: true suites: - "precise" @@ -18,7 +18,7 @@ packages: - "libcap-dev" - "libz-dev" - "libbz2-dev" -reference_datetime: "2013-06-01 00:00:00" +reference_datetime: "2015-06-01 00:00:00" remotes: - "url": "https://github.com/bitcoin/bitcoin.git" "dir": "bitcoin" @@ -120,7 +120,7 @@ script: | popd make deploy - ${WRAP_DIR}/dmg dmg Bitcoin-Qt.dmg ${OUTDIR}/${DISTNAME}-osx-unsigned.dmg + ${WRAP_DIR}/dmg dmg Bitcoin-Core.dmg ${OUTDIR}/${DISTNAME}-osx-unsigned.dmg cd installed find . -name "lib*.la" -delete diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml index 2d72f7b6e5..20cfb29859 100644 --- a/contrib/gitian-descriptors/gitian-win.yml +++ b/contrib/gitian-descriptors/gitian-win.yml @@ -1,5 +1,5 @@ --- -name: "bitcoin-win-0.10" +name: "bitcoin-win-0.12" enable_cache: true suites: - "precise" @@ -18,7 +18,7 @@ packages: - "g++-mingw-w64" - "nsis" - "zip" -reference_datetime: "2013-06-01 00:00:00" +reference_datetime: "2015-06-01 00:00:00" remotes: - "url": "https://github.com/bitcoin/bitcoin.git" "dir": "bitcoin" diff --git a/contrib/gitian-downloader/erkmos.pgp b/contrib/gitian-downloader/erkmos.pgp Binary files differnew file mode 100644 index 0000000000..9d3f060627 --- /dev/null +++ b/contrib/gitian-downloader/erkmos.pgp diff --git a/contrib/gitian-downloader/linux-download-config b/contrib/gitian-downloader/linux-download-config index f5e6382b84..367d4c4216 100644 --- a/contrib/gitian-downloader/linux-download-config +++ b/contrib/gitian-downloader/linux-download-config @@ -40,3 +40,6 @@ signers: C060A6635913D98A3587D7DB1C2491FFEB0EF770: name: "Cory Fields" key: "cfields" + 37EC7D7B0A217CDB4B4E007E7FAB114267E4FA04: + name: "Peter Todd" + key: "petertodd" diff --git a/contrib/gitian-downloader/luke-jr-key.pgp b/contrib/gitian-downloader/luke-jr-key.pgp Binary files differindex 275b041d20..4406e6d5be 100644 --- a/contrib/gitian-downloader/luke-jr-key.pgp +++ b/contrib/gitian-downloader/luke-jr-key.pgp diff --git a/contrib/gitian-downloader/petertodd-key.pgp b/contrib/gitian-downloader/petertodd-key.pgp new file mode 100644 index 0000000000..5ee82a6f7e --- /dev/null +++ b/contrib/gitian-downloader/petertodd-key.pgp @@ -0,0 +1,1901 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +BSGsRbgvDNIR6RczbQ3j53l5j7ReYKP1yLnPwiNC3noPewqGephy6gH3BjYCtYZt +Vq4+OeNgCLi5fhy1xNCeqto6qQlbms7hw5YKB/x12QnsdgxGV9guuKYiJEOG37A0 +qdmYOBkHt3TfZWlLCDVRaHvb30eGSKS01aHmK4pJLAFYVgLw/ggBjegj6AxdnpT5 +NQM9uWCfwZ2qa3vBPaw7FYhjcB/nSvpZ9I4dC1F3lffLucgpg73p6OO5aU6w952M +hGDMKBGovbG2hvIaqs1RwaKZNtZZcBCXfIsQnGU11LYs/3w8cSyxWE/35HlveXW6 ++YBXHIaY8nrJCQHwLlmRj7w1RXVODBy75iUd6mYURPZyadpVfTKIpFkddIFPLKPl +6QkByMAno4dWS/p/2/xek9zBnhUhBjiQ+F9+rbF0K5LswiMK/BwzB8UXOXeueZXH +H1vDCDOBzrDit8bgxJy8j2YGT2Vq7ThMnhSLLC3hRcWJAz+0rSJ51OxWLS3CQGgQ +lmMOc2Wmz+K7JKvat3eZesBTQEZre6MpxXiebna53RnK/jFlEiqeZqPDWfry81ew +qzDnWaQ/m43L0V8QVVeJBLEEEgECAJsFAlGVbM6UGmh0dHA6Ly93d3cuamVuc2Vy +YXQuZGUvZmlsZXMvb3BlbnBncC9BNEZGMjI3OS1jZXJ0LXBvbGljeS0yMDEzLTA1 +LTE3LnR4dD9zaGE1MTJzdW09OGM4NTgyZjI1Mjk5MjRkZDY5Mjk3Yjc0YzM0MGEw +YTI5YTFjMWQ3NWIxODFiZmY2MDJmZGRkNzllN2YwNDk2NAAKCRBOH3mapP8ieSuD +H/90vmcaUOrfHXiDON3WzGU9lkk2K5GEauttX6p2gRRhzTqy47zUl5Wu8KWetkft +XOf/4yhPwDyFzCElArReLBg0gM4vgCAH+jeZqSWcMvgv9M11cA8rSF7aRLdpPfR6 +m4RwS8niwpvInNG4DNCQxqZqSBB6XDyrtmjJP6PNhJD6Nd24nMzImyJ7QB1JrSjK +lk539GS7fgEhhrBh5nKhVlvzzusvAc7MKysUCMduQrRYbkWQxw41OdIlIZMc6ErC +SGEEKgrVB4xFZR2DNTd93PDggjHTtvXKnydCZC1XL94qvvHMmSagXfk76QwtHBG8 +099QR60buDGLnZvyDbR7H5ts7UPbIwIRWA0v/p7hhT9PRRLLVuK1oLUa1gEN/IAf +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 +peBNXREPb4kkfF+RpT7HWD0u+i6sWb5+Yzt+vuRip7JkVu+7FDefLJ6ke0nEu6xJ ++HbAmZznOjnAg3o8M+XD9NIJT8Z5yB+ikQ3AZuZ1bJfNC85++oKGq14xJSvcA/4f +hLdGNmj05ijBMYLsdzDL+Wkdl/69sultxs4nD6mhlw2epZtiw5qj4dgbzz7p6O78 +3pZgXBvy5UFxgxx8hZsjDAayodfyebFNSLXC06uVjafFbtbQZc83UfJAxwaW0jF6 +cl0n7nquGPk1TNMVCHOd9VNCdhPlFJHctUbIv0+HNrzgWCrmN1JzP4BJZsIK/Oqs +j1SoFdGwxRWpSNwgO3WJASIEEAECAAwFAlKOadAFAwASdQAACgkQlxC4m8pXrXz8 +wQgAj0li8ga7DxoPNue6DzfKUbXw/LqjZUgs+d/MlHiEXS9hEMBDKLAnpDVIwSQl +Ldu7UvanjpWK5UVj4jTMnNKjJXHRlFc1WBRrehOFej6tob//cd3czFKDG+IewnQX +OLI7TA3uP5rg9PikGJOUW7wHUwunBdQl9XpLJe2XIhR7BAC4ppYl14OLDXM87f4J +grEDv8po/xdY+s2ysI31g8/v8mg2ROromZ12HMrsirqyVb+dyNFxcQi9U9ZC6h8V +00G4+duTjJt8YpQ/DYybVpMmVa40ePV+Z113OFZn9JdKs3p0kSi8csyLyItyJ62K +c3/yfspKE3ZDrd+FVJ/VJXUIXohGBBARAgAGBQJTGOluAAoJEHc3YWR7U2QV/1AA +n1bPtu6ZiSp+10U6yMmEvc9jWGlbAJ9tSDXgb9k3bFr1scJJ5kBsFJLGJYkCHAQQ +AQoABgUCUp+JYAAKCRCkTD3TpUjY4lRZEACqIACFZ0JHma1seks08qGrOYAIJxd7 +VS7Pgk4w8/kYaoXePs+F2Liaevnd06aCytJBxa32yR9fsgoPwoF2I4tRMCBn68Sn +lw/te9SU7M9Prrf8Rq02gEO7a2kqAH6ZNcUHdQDw2U0Rj4ArnZkHUh2Ey4TJwVUc +tZzeXxlpgqYRTBLNaJyyBP53N8A+nHtpcqJYdxDYMMdTYVNs8epvRMcRfO/dWnnR +lnco1ptge3cwm7E1/zX96/7GTPDwvtWseo0PF9W6wbaWTNm3c71iDXJbNbcVWdFt +BEJZBhHzciyN3sysHEI6IwGzmmpAKpQJ2foXpYhxy1LuHBT3kW7dq/mOnr1zyjkI +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 +TnaxBZk5kjROOetXQapW02twdC4k9tXLzuxjZ5w4zul9j0DFvXXIENHRMZVQDODr +e9SkkzfvYyhqLMB5hFMZK7bnY6LvtWWM+aLZRa1B/j+thWwhM0JnCyKtqOXS8sGK +rfpj7et2f66fi+i2yNhk/UBBHGwNlhYkeZG+ovcWTAtRjdGJASIEEAECAAwFAlQX +PQ4FAwASdQAACgkQlxC4m8pXrXyvNQgAq8NazgIJu3cuGoIkel7tDAdPjFtQuznc +pARNRSwVyG2jdZHOrNf76AoZK0NIqcenuKVyLe/o/U9+P4ufbBPajqnhSqEiu4f0 +mk1TWoJxWC2jW+Ew2ZLNu0adtoAqBE+FDCieAObUKF++4cxiU4r5npilADucQ4Fy +UGLTA5RUQy4j9w0rQJ4xCAhzxBT/SiULlKQgEpj2XdOOiHSMS76fbIkw991L8ECN +/1DiNDAzbExE2BwS0WwPb3KUiT0ookS91Q2i6mWaJbCa4iJUvK3i2YTxPpFb4N2e +YmCCvKMLT4RC/2JQlUSjEozMImdYbFh1c0snzXmRatWK9THyyFuXx9HTt9O1ARAA +AQEAAAAAAAAAAAAAAAD/2P/gABBKRklGAAEBAQBIAEgAAP/bAEMAEAsMDgwKEA4N +DhIREBMYKRsYFhYYMiQmHik7ND49OjQ5OEFJXlBBRVlGODlSb1NZYWRpamk/T3N7 +cmZ6XmdpZf/bAEMBERISGBUYMBsbMGVDOUNlZWVlZWVlZWVlZWVlZWVlZWVlZWVl +ZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZf/AABEIASAA8AMBIgACEQEDEQH/ +xAAaAAACAwEBAAAAAAAAAAAAAAADBAABAgUG/8QAMBAAAgIBBAAGAQMFAAIDAAAA +AQIAAxEEEiExBRMiQVFhcRQygQYjQpGhFbEzUsH/xAAYAQEBAQEBAAAAAAAAAAAA +AAAAAQIDBP/EAB8RAQEBAQADAAIDAAAAAAAAAAABEQISITFBUQMiMv/aAAwDAQAC +EQMRAD8AyohAJSiEUTyuy1E2BIomgIRAJoCWBLAgQCXiSXAmJJckCSSSZgSSZZ1X +kkCZ8+v2YQCyTC2ofcTW4QLkkEuVEkxJJAkmJckDOJWJuTEKGRMkQuJREgERMkQp +EwRAERFr+RG2EXuEirUQiiZUTYEqNATYEoCaAgSalS4ElypCcQixMPYqD1HEV1Wq +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 +U2SsTgUAXL3GANeTM4N/XzzPXokCHAQQAQgABgUCUy3cVAAKCRBgtDFx2LpfQac8 +D/4kXKRRhtT8U+ViibJHXE8/MpGIqF373wbfs2y9S3D+kd/JOxC6ibnev2b+Wq29 +qdMk6BcyUaVzP0dxcnJj2CygYl2RD5dfIp8PzSilTHNfXGbJr9xPTcAjamXhUoJZ +p8/C1ODwf2y11EB8CLDVVZmqgtxUOJNfh6is3cfzbYF8O2CX9ai5x9UykV9g//// +//////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////// +/////////////////////////////////////////////4kBHAQTAQIABgUCVK48 +ZgAKCRA82MB/C1zhTgoVB/9j6/khep6CXjyIoghxycm/4qhuCs2M8kM6G1B2W0sv +lkaeqha+A96lCS43YoaRWkto2k496rrHfoC5f/8Vb36nQS7hd59DYANbtr5KJzwf +yYsP/7+bkaiuC1E5SqmHjFiYYe7enQWdrcbsDfj420ScL9t2P0gO3vKJ5S8m7hkc +a4OdCmYJkdaYxJcHK7BC4vkJmbxfnmmyGJaEPP/oxNCeZiyL37Y62N4DqCQpDj5C +Ngk5w93+6H4oI8CIMzWEQZt9bZ2k9ipGobYhaLgVk5UMWkVJ8CuZESCgHLCuz7hG +gDoDmXTnz3zbp038QPusF9zb307DEsWq7XwJB33/ljm4uQENBE+Xo6MBCADB0197 +HdK48SqvipyPB49vlzfP6KHfcACZCg1JTqOt8KRJFwTP3PTi/GJuUvt4io1UVG29 ++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 +IemulKZaOJ7LLNmKTjOZnj12+S76emwpNEduQajYMDXPtHnc1FpF2jo4tfOQBO5h +7z+UJzsGPO9O0pkYPxO3h/RS12R3uyvVE8Ej0w/8wiPFA29QgGf3zBRJ6GK2rAV+ +N/qhlMnE0agSy2+cmZzPDe33gtjLVZ7YmVp0YxbKa35DtvukhliGKH1cqkLKUt6Y +ytvzuckpEQPEtI/7KTYmCfaT2hlOo7uSfGn9FtV0xzlcVy0dMk24xKllyeyeennJ +jOuudVo5CQFRssnBDbkBDQRTG+drAQgAlo+QtN3XwYnuaRheTFnOLD7h9XBqZapO +DwqQbod7MXZqp4jEfGqfQKleL0Xs9Dh5I66Hhwr9GIuciVSQbSEcqBEznHJGM6n1 +ssG662b5a+W3WC11SUGpuZJOUgOEHVLgVqSe7td8w8bFD841kfPXRTYmtNgzki/K +RK7FFR1M5C5fBaQb7KGH/Cj/jUKKS9EwrMTW4jQxdcz881MFOGLM9hB4MCRXv+W4 +yUGqXurvFaEvYzVrYF1XNJFHheQ0Iw5GNUGxk0+F01kTBRNjJ+fnu/G/rIkHUYAc +E3VHqsd1l6NStMwn2o2a+po/xeS3CIdxsRBx4NYepBrZdRjUrRzNiQARAQABiQGE +BBgBCABuBQJTG+drXhSAAAAAABUAQGJsb2NraGFzaEBiaXRjb2luLm9yZzAwMDAw +MDAwMDAwMDAwMDBmNGY1YmEzMzQ3OTFhNDEwMjkxN2U0ZDNmMjJmNmFkN2YyYzRm +MTVkOTczMDdmZTICGyAFCQlmAYAACgkQf6sRQmfk+gTTNwf/XK8IGaPMASckFU1c +EHM8ruYAC4b1wzzAY2733geJJlIMd4pYVhPWcczwMozc2AWJrhHzMS6Ho/r+FmAQ +H+s0csYdhw0hL8w60bPXHSWu9wu6zlUJEY8G4pKjisEokYY7Aje/oEGfg8KJLHXA +hYrPauW++O/NFCZ6ZNimFf40JRSjSUGzTKuBYW0/25dfV017De1XZvD+Qd198CfA +9ojvBP2Rn7PYQkCMYA7xsViNz+tD0eC+BRdyO0cffCzoOOHHXy/1KSOnCry+JReA +jnpviOsUyfY2bqGsmOpxyo5VfZ3OoHOOV7nucBEmQojuhl/xvU+Lc2airaR6UIAm +jWDWrrkBjQRTG+GcAQwAk8T4liMdVQMjh6WFsmY38lEGu5FxirVSrMKjVQAq52XO +l0I2jc6zTdqzyinW0sEswcQhmkTANzCspaU2kIBr/QVbdtPxPakwysV3RsqHPlAI +rVQIdBU7ENnE9uzRzJtZfc5m+buknQ3MBjMdXlAceO4w7i+uhu5zXW4tSVwgKzhU +uy1IexvK7ni4EZHzsOAarFbZpHozUqNZol7ALJrWkpQWqjuJZEIN0vefgSf3AV/r +vGFlbI/PwEWDZlYlwDV9scuHRqqu0S1LArcLDmf78vnnclZYVUNl2kaVgRKmu2ra +CWCSctmkyC5rqM9Xj+6B0ZVxhXReqfCnRn06Zwf7WwJuMWMfbAfZjDbksIMgKWpT +/8UrH2RrI/6b4w5hXa7/C8zp8H109f7xcRgdLyj748P0bsrrVSfUWxMP+XRRGPY0 +4spkhNUVFlYc2BroIRXS+REKuI5WfWnUeSNWfAqbexaHRSgQQIydN2qp2sSxAe6Q +FE3aXZyCHC4DMqIlUml7ABEBAAGJA7IEGAEIAG4FAlMb4ZxeFIAAAAAAFQBAYmxv +Y2toYXNoQGJpdGNvaW4ub3JnMDAwMDAwMDAwMDAwMDAwMGY0ZjViYTMzNDc5MWE0 +MTAyOTE3ZTRkM2YyMmY2YWQ3ZjJjNGYxNWQ5NzMwN2ZlMgIbAgUJCWYBgAI4CRB/ +qxFCZ+T6BMFsIAQZAQgAlQUCUxvhnF4UgAAAAAAVAEBibG9ja2hhc2hAYml0Y29p +bi5vcmcwMDAwMDAwMDAwMDAwMDAwZjRmNWJhMzM0NzkxYTQxMDI5MTdlNGQzZjIy +ZjZhZDdmMmM0ZjE1ZDk3MzA3ZmUyLxSAAAAAABUAEXBrYS1hZGRyZXNzQGdudXBn +Lm9yZ3BldGVAcGV0ZXJ0b2Qub3JnAAoJEGJeboN5AaHKDGcL/R3/aZoKC3OgtAkb +o2g/ykbMt8pIko2cofAB/3zwl1p2qvwO3QV/vTVt6y4uEPEHItaFLiKTSH2KpTVl +YflOh4M2/LXwXQmOiixpyDY3w42R6AZqY4g6q2SLDpNQLWaxVOerPgvDpSkIbcwH +j8Fvai3Mrxtr2J+Jq3KW6TQnW9m5G/j4SqmQ2sRfkl6EEHiGcZp7pbKkE+qYGsmV +HEirfCjlf03N3c7zcs6Nic9AEq2ghcnLwRAqmyRU59wOvIHlbQtnpJ9TI2zaOp2n +U+4wOVkdolzN6TfPPLwATXsUFlQRS0ygarQXF4ifZ55MGZ56pc3GOR6fEH0FaUbr +AhLvdN/fKOpRkQ5ITzxY8rP402lP3V1gAInRaEEsXKFxnQn/sCbLgi2dlaiqeErw ++Stz8bANhaS7uja4KBG00aHH87IF22tKsC29q5USR0axfEAaDMEaEJxtE76HoIKR +CovUoS2TfBJHlJOqzuQR76v++QAjqgELbHqUudhv05yQ18t2otJ8B/9XusAN7sya +dCI/Kkj/A9WL5ky8Jh14oSeuIq6G1Q4WN+BcYAtGroTHlo0zMmQlkBfOb1Ketu4o +KWoDQwPUle+WsdRv2GklAURakqqjZWCeV2JmjfNML0MAQ0iF5g5W6BeSdnTVfBNG +Zjq0lD8d1+fVuyWQ/Sl9Za/dfGemi07X4pN2+4AWk06b5XmyPyreoheFCBIFRNxS +UJuB3HOHx96seBv0JPyYZyWqomJ5TAxubK+T+l4IPebMEM6cEo1+Ut+vS2xG+kuV +U0hxDZFMkgjD3a4Od06MfUPNhxIXGFoPTdESXdKDsiSykr6B3mkFX0XSPtfrGemY +SLuZHeehD4iXuQGNBFTbQXkBDADPxI2gCAy4SYLcK/I4hoydjmsXBpWHmHZZUKvO ++6GS8tXX+IlzxihQy8Nc7M4hL8RjBBMIqiF4ogEqNvIzD+LWYM3+K/Q7Zmsaq77l +iNS4BXUn1O/3n8WPNCJIAPf2ULzZi1ucVnTCj3AWxwikhIZnqV4c3Xy5pGpLRbaI +pg3yfLsQYHlAW+Eg3VqrX4YHgPGY5p42Qo2VejxO+7q7crkDd0GsCcGyL6MEVA9F +pnQ1WugmWZAlNfDNeySdYxwaISIvesVhDG4LvU3o7yk2rTOL4iDNGfRTipLuZ8D2 +3EABz5QvBIOqrV0o0pMfGjUMXSWPVeSuxnvL4+atE8bapOJIZtxYwcYV8cXG0HZw +zvbw4+wIw2mSPBTpKLDMc7m8l+yq9KhOzILRHDSPEt3z2TGtK6oerKizOwKhX8dn +2PspUlY2RmTAauvqWOYeg7aEX3DmI03Xu+eVpjPZ0jj15+JvXsa9yI0Z8gEggIbw ++T9KzGrXv/nSjTZ2630HIa5bxI0AEQEAAYkCxAQYAQgADwUCVNtBeQIbAgUJCWYB +gAGpCRB/qxFCZ+T6BMDdIAQZAQgABgUCVNtBeQAKCRBm5IaDjxmqELvbDACZfbX9 +W5ZwPYRbI9J4+es5Fu2WAFe8WcDNZLfyFZRe9cCbWsxtKicmmTkVpVSTHSV8+z16 +WeOuaGh3dl6Sd3sfKlBjVSMjTRWKEGJCSAAeTfL6Ag9BR6/r6Bj0nYQzm1Cga9/y +j63YZfmSiADS+BGEVtBVFkspYHXLi9U1UeCjTj9qgbN5eWGq1fOIJ7EQ6IVMJpgd ++/PbFd5J3kjeFl9Lageu7PjHgH+vAnmw4jy0D19DwO6pQduc6tWd/8WcWZHVuFf8 +CsJA82sk2B1R+SdYbTU0vljCv/vRUPOKM3zzap7PADpYLf49xQTYp9uYmLvMv5m2 +0NagUXggdJ3XN9QipBAZsKjp8c0HdW4E0YOt0G1oHdUc039Y0yyMvifaIPoHhA0B +cS7xs8bmrn+IA/oFiEnB/PUsFEL2fPSA8+2zSVoFGZYoGqbkPScNA+uRZmvqL6W8 +hSMXiE6/ekZL9+AbIuP/BRgcu284rrmu+KDPbz6WlWEtPbshfLWbFwE3YqElHQf+ +L7g/C9tTZrb6kuYOuKC+JsTiYzz52gniEf7ZXJswxnXAkkVy6tvxn6ASms/VWmEz +h7FCncQRbTFqy4+4c2fxSWGhDBp1u10/HNG/66sd+9vhMBAvLBV6FeLhudDHHPVV +UvzN0vlC+FuhIHkqGITkSBgXGAgCR1KQr1ixTT6vHRtbBQLqNBR7e+fUyzkFja2i +ynM3UfDg8kwaPDUNTTLRrACPsTq7iGUHmnUiLUYK0wKCqXb6O17tiX+qozcholyz +OL8TVeXVFoWmSozEgVn4j5rK1WWFSwa0ua5zQC7h+TuP1+Se/SYqn2ii75eBK9Y7 +/5WVebk6ur71eH7eIWk+XrkBDQRU20HWAQgAhOI61Edm8YCDU5ZwUy8FJWLS6v1C +V1y5fEdne3RGH0iyP1klfnLkF2CqVZmaa2NR6UcT/vAqbUr9JKPBDcJvYUhoifIJ +3lzG6tOfTLTfxongUdzE6WlNlMMjvxDDPwQgheECP4f4UFl8Sy/EVn1akJ/TpVoh +cDceCVxCjLPnJPAbTu/SgEo0YbJxMEb+cv80fyJXgMc5uUEfTVIbu6/pR6A2NHFE +Mxh0F5YIPu5IXH+by14ckydIOgJzOjp2usZBkqraVUOxzeE/U3D5HZ8IBs0znsAh ++xg2zjR1ppu7WQ8nKK3N/pMakivh7x4Za1KXq7IB5wlIR9Sb0J7kABZe6QARAQAB +iQElBBgBCAAPBQJU20HWAhsgBQkJZgGAAAoJEH+rEUJn5PoE/8cH/2ghYCq4WTjr +n/go5mC+USMTsdj0haMcZRoOs5+PP/eLpqWJTXNbFGjyHIKLGUvUZSsLN/V41RJK +HPuPdxNzvo4YndA/LwLJPl+MLjM+0bgiIUJytdui+YonmfS/TNkPajCrwE2KqKVs +nIX/W/w79o+fWxppwLlEsQ2kI+zMqFi600azfCDUwfy1ti+sC5F/ZDsGoDgFpxV4 +Ao3RaDb9ZxOLY3XPERRjtczwFAj1bw8ZCNyWv6DdeG9028/r+fAjZqn8kz1Ziv4V +WvnBgkkOLTkNmIkey+CJyYemo1ks+x1sgQYspmnv7rkDRLBL1dg9UQmeRvqB0FsH +vOq9VY4ZxTm5AQ0EVN1LOgEIAJBvsHt8coDvBL6bq7A0QROsGEOGE2/Wd2TgEdCA +eZeKMseUCwS7ssJIeYciRbidxbl21fyNVGuKuIz6JMCbS+m4Uv0LKYhXKdVkfkgb +9TNCXm0lKbu5GjOOYsfhdRc4FJUbrb8ObyfDoySKMEOACQY94FdR4wjLmdvvmyb7 +YICxar2KFaYSS8joKXYC5PRPZInHF/jhI3ODWjoZSn2Rg7ARN+tR55jx10Ex/4iX +zRd5cax418fG5rMcv3Er8kOmFyremIsASH1E1oBm/We6jyowfHBrp2dy9aobR9GU +jwPh16GBaHYQeXt0uJrr83RC430dFHsrmx8svbYuYevtwvkAEQEAAYkCRAQYAQgA +DwUCVN1LOgIbAgUJCWYBgAEpCRB/qxFCZ+T6BMBdIAQZAQgABgUCVN1LOgAKCRDA +hfIc5/S53Op1B/9+JSyXipINM6qEMEAj46ea8EpF6p+vFFHJH2cq67ILc/Doznyd +7pogxn7QFCrSDfwoxEU1ruDM+hcoxQBjYSQ1sxu1fCVBtSD55V1AQBp6xz+MJb33 +ISTEBpEqoAstxW3WxsUOa/zju9q27C/GsX1uU6DW/6NE2FhZHDFwxqwEq0ouRcIU +58nOkQkOUEfC6FLfU0H+kNAWmUQcIUPdVwP7J5vxTqnsXnSs81QHdkXOY8VROSfD +uIHTW9ieTe6jaZ4NP2IjsTRCbSoDvspjwg/KGPG49b0Z5XCvo7p0YtkNgYXjAlRO +mGRc68/xTacMEb1cfiSFpdzTakbrrMBpkV23PGUH/12R/oqjzkYJzMVLlhdgv8NQ +wII8mWE2FJwFzC8w5jL1uYSE1wjuuBQcf313F/KdiTq0thw90Wh+mIHJ+7vTQhBC +HFFgpxfXPxXu01J0wCl9XqztV20s4WBXltbubPX1xc2QBNSPIqiB8s32mMiHKNvZ +owxFZMq2PyZ0hB1W+FK2qbf8pZf+2vsEuqLj96ftt17jr2BoyytQeboYennOMgYZ +UtzuhpOAhyzzbCOxc8O5+OJOkYbGtCZpl5oH94fvKD9BV2hzGm/SemJXtBUAIu8I +cuKonwxwerdCP3ftxT7iZNU1op4sZWD3qA0jVs/mbHhh40xo9ZhKPAaI/Le2nS65 +AQ0EVN1LrwEIAKHTFbesEf27YWIBN0ZDphhLp/jHkFbDhx332zPefH/WD9QRwY+w +F3CHh0IYDaa75cq5LCHFRdXCbZ4jXGnWomNlKtfs4XokWYElDL5MQpT4zbtFYhSK +seAflHf6Z7TRK/nplmDXs82J/NNen8Ati84Sqb2CGCkD02SXVBrWJBTFyGzkJl6O +onoZph/RSt+rXBKFYI8m5csHwwQdZRYk7FNdIHmvb9SCVPq0/FD4x0i8xNrGws1E +EGBp3lbaeuAPcFoWUnl9NNckp7r6hX8TKOUU4z7+FwXY7xnZd+h/I675MZ8typ+r +PSpw2hgp4s1LXNZRBer5K+4VGPdfRH3sBdUAEQEAAYkBJQQYAQgADwUCVN1LrwIb +IAUJCWYBgAAKCRB/qxFCZ+T6BN3LB/4oxQk1UUxIa6mKdRUFjk0BUc8pQsNWqTAw +xL+OdUSgZOgr2JCSICSNAShI4226tTkfGhJyp+DtzlF2bm78LibQI7PArzxD42l6 +93XtkHdG7MqBxsjf6AGTm5c5fJ76jbRMAXu4MBvdRe2PJDCCl2D+YEJzcu9lfW44 +8CHdy8ta5njgtdc29+S+2V904bgc/tYRn/OscGe0Hm1CeNn9Rzs3HrOG6Of0kJ6G +T0ipHSIhttHTekpYFlekpHjhSxvkDEb64BFyj2YBJSmX4EhQVnTeQUIfz+mDmgQ2 +w0qGnckIQ73fP9nYhlsyjiqbxkzOELDKt73ZTI+tIyOYW/sbX2tP +=BHZ+ +-----END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/gitian-downloader/prab-key.pgp b/contrib/gitian-downloader/prab-key.pgp new file mode 100644 index 0000000000..0940d15c6a --- /dev/null +++ b/contrib/gitian-downloader/prab-key.pgp @@ -0,0 +1,29 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v2 + +mQINBFI3uQUBEADRjbUQb65n7sfjT5OPnjPO8oUh5onKjNALRGHGJxWwNkwkgmT6 +mZFWjikM8B3ONEp8MfTyoOzv+MbWuBDS1GZxi0Tcb4HU323/7hhwfXV4bcHif7vq +Sc1ahN/5LIHj0htC6Uj78IWXW+kPWjX9biRJnZ9eerfA/AatC+4KuvYTjAa9uAfa +BnAs38EG/7ryXzxdz6M8iNB/YjHE72swSH84uTtA3LqI3huVY41eFMK6qXFBMFiq +H6bMx4pjwKzJj78bibXEQxq88yc6TLxbURs2GF4s11dr7Iq2Y+6FHqX3PfUByZBz +PQfbEy1Df4RbB3htCBv5puETlqZ0PVe9/B+WrcnaobxrbKEAqt0DspfoveRTIL+z ++YDs7FuW9TFBmd+5d2nmblHrNm8eEig3DpOoVetuuXsmDy03Fwao7hGkva4P3xbP +H4/U8GaxsfNzuLvEyy+dtd2t2C1HIxS+56r41/vdb/9rvGgEQuSr0DVpZIW9en0f +3bek+H7/qRCbXuaiBACBvOKNror5jtTeXTvnHWMkrOItyGH9pwR2Lhhv68JQS1jk +e3pwVRzfHBz4wQMHeLIh+blsVKCIjytBR8Rq36rsmN1q44/3HwuPW/XP61kPP90k +dKyPF4Qa+EoPw5ON5nr3lWy8ysklM79o1NmpyqNT4UjtDDBSfJtX82ct8QARAQAB +tB9QYXVsIFJhYmFoeSA8UFJhYmFoeUBnbWFpbC5jb20+iQI/BBMBAgApBQJSN7kF +AhsPBQkDwdj7BwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQ0cuiohvNiPaB +KA//Y6h6r8vUkA4/Gl2ejMgH+/DAolg9dFD+KMjQNKw16g7WzFNNS05T4RhcfZDT +mapAW2LOUiiPVJTacmblyFp2hXCv3S+DESQjVdz15pxMHkt9fFteXGyJyrcTY5pR +rZmjMD/9Twy2mLl5IH5ms87p0TN9HhM5Ux+B51la+Uq1wMdc1PdPWvGDeBVxCnRI +w0224M5u1uHaMwMDGdz6sXuCuonB7CDGL9Z2+m1Al7t0peL2QdKHjv8S+SKM2rZS +bRJyNmmTSKFQOTb9e2Ve48NNeaC0usEf8ttsygclps0mDpoa68YSY1LiuwVoIxvd +S626eDqgmq1yyz0l3gQbYKIUv7KvRDnYhqIEkPZLCOwKirm+I5vzXvVIlfoNRoyJ +VH3K6MJDSpBEerYNWHmUQpp7cLXIqVtako7IgmglOXQ5XVRnPvlOt5VOqQnNRTdM +nd/FK3n1TbUyNRverODSpOS6ZxdSSwLkTycTtj1SvpLo7laW0HQ1ofJfwzVmOq8s +8aWWTwBBC/X1UNLL/rsZMoHeUpaKHF2HSK5XzLcwqOBPRTMY0OG7vtBvj4G8clht +A3uQmmCMr6RxsPPYPHPO3MnMpY2AK6RRnNZg5Y1Fu+/71FAuUDVTxmI402yE5XbP +ILbJ8RgsrTVgeHdNhp9or0BdsB/wwMT5intkCaNqwb6eJag= +=y2Va +-----END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/gitian-downloader/win32-download-config b/contrib/gitian-downloader/win32-download-config index 06c164180d..ef47918a1f 100644 --- a/contrib/gitian-downloader/win32-download-config +++ b/contrib/gitian-downloader/win32-download-config @@ -40,3 +40,6 @@ signers: C060A6635913D98A3587D7DB1C2491FFEB0EF770: name: "Cory Fields" key: "cfields" + 37EC7D7B0A217CDB4B4E007E7FAB114267E4FA04: + name: "Peter Todd" + key: "petertodd" diff --git a/contrib/init/bitcoind.openrc b/contrib/init/bitcoind.openrc index b0ac5e31e1..a94f03680d 100644 --- a/contrib/init/bitcoind.openrc +++ b/contrib/init/bitcoind.openrc @@ -19,7 +19,7 @@ BITCOIND_NICE=${BITCOIND_NICE:-${NICELEVEL:-0}} BITCOIND_OPTS="${BITCOIND_OPTS:-${BITCOIN_OPTS}}" name="Bitcoin Core Daemon" -description="Bitcoin crypto-currency p2p network daemon" +description="Bitcoin cryptocurrency P2P network daemon" command="/usr/bin/bitcoind" command_args="-pid=\"${BITCOIND_PIDFILE}\" \ diff --git a/contrib/linearize/example-linearize.cfg b/contrib/linearize/example-linearize.cfg index e0fef13886..38da02e66c 100644 --- a/contrib/linearize/example-linearize.cfg +++ b/contrib/linearize/example-linearize.cfg @@ -4,13 +4,23 @@ rpcuser=someuser rpcpassword=somepassword host=127.0.0.1 port=8332 +#port=18332 # bootstrap.dat hashlist settings (linearize-hashes) max_height=313000 # bootstrap.dat input/output settings (linearize-data) + +# mainnet netmagic=f9beb4d9 +genesis=000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f input=/home/example/.bitcoin/blocks + +# testnet +#netmagic=0b110907 +#genesis=000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943 +#input=/home/example/.bitcoin/testnet3/blocks + output_file=/home/example/Downloads/bootstrap.dat hashlist=hashlist.txt split_year=1 diff --git a/contrib/linearize/linearize-data.py b/contrib/linearize/linearize-data.py index a3a1173b1f..7947c6bf72 100755 --- a/contrib/linearize/linearize-data.py +++ b/contrib/linearize/linearize-data.py @@ -205,7 +205,7 @@ class BlockDataCopier: inMagic = inhdr[:4] if (inMagic != self.settings['netmagic']): - print("Invalid magic: " + inMagic) + print("Invalid magic: " + inMagic.encode('hex')) return inLenLE = inhdr[4:] su = struct.unpack("<I", inLenLE) @@ -265,6 +265,8 @@ if __name__ == '__main__': if 'netmagic' not in settings: settings['netmagic'] = 'f9beb4d9' + if 'genesis' not in settings: + settings['genesis'] = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f' if 'input' not in settings: settings['input'] = 'input' if 'hashlist' not in settings: @@ -291,7 +293,7 @@ if __name__ == '__main__': blkindex = get_block_hashes(settings) blkmap = mkblockmap(blkindex) - if not "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" in blkmap: + if not settings['genesis'] in blkmap: print("Genesis block not found in hashlist") else: BlockDataCopier(settings, blkindex, blkmap).run() diff --git a/contrib/macdeploy/Base.lproj/InfoPlist.strings b/contrib/macdeploy/Base.lproj/InfoPlist.strings new file mode 100644 index 0000000000..b259ea141c --- /dev/null +++ b/contrib/macdeploy/Base.lproj/InfoPlist.strings @@ -0,0 +1 @@ +{ CFBundleDisplayName = "Bitcoin Core"; CFBundleName = "Bitcoin Core"; } diff --git a/contrib/macdeploy/DS_Store b/contrib/macdeploy/DS_Store Binary files differindex 099960712a..db9d16f1d7 100644 --- a/contrib/macdeploy/DS_Store +++ b/contrib/macdeploy/DS_Store diff --git a/contrib/macdeploy/README.md b/contrib/macdeploy/README.md index 0aa57b4777..6163734e62 100644 --- a/contrib/macdeploy/README.md +++ b/contrib/macdeploy/README.md @@ -11,5 +11,5 @@ This script should not be run manually, instead, after building as usual: During the process, the disk image window will pop up briefly where the fancy settings are applied. This is normal, please do not interfere. -When finished, it will produce `Bitcoin-Qt.dmg`. +When finished, it will produce `Bitcoin-Core.dmg`. diff --git a/contrib/macdeploy/detached-sig-apply.sh b/contrib/macdeploy/detached-sig-apply.sh index 7b3eb1b197..169f690438 100755 --- a/contrib/macdeploy/detached-sig-apply.sh +++ b/contrib/macdeploy/detached-sig-apply.sh @@ -1,11 +1,10 @@ #!/bin/sh set -e -UNSIGNED=$1 -SIGNATURE=$2 +UNSIGNED="$1" +SIGNATURE="$2" ARCH=x86_64 ROOTDIR=dist -BUNDLE=${ROOTDIR}/Bitcoin-Qt.app TEMPDIR=signed.temp OUTDIR=signed-app @@ -31,21 +30,21 @@ if [ -z "${CODESIGN_ALLOCATE}" ]; then CODESIGN_ALLOCATE=${TEMPDIR}/codesign_allocate fi -for i in `find ${TEMPDIR} -name "*.sign"`; do - SIZE=`stat -c %s ${i}` - TARGET_FILE=`echo ${i} | sed 's/\.sign$//'` +find ${TEMPDIR} -name "*.sign" | while read i; do + SIZE=`stat -c %s "${i}"` + TARGET_FILE="`echo "${i}" | sed 's/\.sign$//'`" echo "Allocating space for the signature of size ${SIZE} in ${TARGET_FILE}" - ${CODESIGN_ALLOCATE} -i ${TARGET_FILE} -a ${ARCH} ${SIZE} -o ${i}.tmp + ${CODESIGN_ALLOCATE} -i "${TARGET_FILE}" -a ${ARCH} ${SIZE} -o "${i}.tmp" - OFFSET=`${PAGESTUFF} ${i}.tmp -p | tail -2 | grep offset | sed 's/[^0-9]*//g'` + OFFSET=`${PAGESTUFF} "${i}.tmp" -p | tail -2 | grep offset | sed 's/[^0-9]*//g'` if [ -z ${QUIET} ]; then echo "Attaching signature at offset ${OFFSET}" fi - dd if=$i of=${i}.tmp bs=1 seek=${OFFSET} count=${SIZE} 2>/dev/null - mv ${i}.tmp ${TARGET_FILE} - rm ${i} + dd if="$i" of="${i}.tmp" bs=1 seek=${OFFSET} count=${SIZE} 2>/dev/null + mv "${i}.tmp" "${TARGET_FILE}" + rm "${i}" echo "Success." done mv ${TEMPDIR}/${ROOTDIR} ${OUTDIR} diff --git a/contrib/macdeploy/detached-sig-create.sh b/contrib/macdeploy/detached-sig-create.sh index aff4f08da8..18f8661cea 100755 --- a/contrib/macdeploy/detached-sig-create.sh +++ b/contrib/macdeploy/detached-sig-create.sh @@ -2,7 +2,7 @@ set -e ROOTDIR=dist -BUNDLE=${ROOTDIR}/Bitcoin-Qt.app +BUNDLE="${ROOTDIR}/Bitcoin-Qt.app" CODESIGN=codesign TEMPDIR=sign.temp TEMPLIST=${TEMPDIR}/signatures.txt @@ -19,19 +19,19 @@ mkdir -p ${TEMPDIR} ${CODESIGN} -f --file-list ${TEMPLIST} "$@" "${BUNDLE}" -for i in `grep -v CodeResources ${TEMPLIST}`; do - TARGETFILE="${BUNDLE}/`echo ${i} | sed "s|.*${BUNDLE}/||"`" - SIZE=`pagestuff $i -p | tail -2 | grep size | sed 's/[^0-9]*//g'` - OFFSET=`pagestuff $i -p | tail -2 | grep offset | sed 's/[^0-9]*//g'` +grep -v CodeResources < "${TEMPLIST}" | while read i; do + TARGETFILE="${BUNDLE}/`echo "${i}" | sed "s|.*${BUNDLE}/||"`" + SIZE=`pagestuff "$i" -p | tail -2 | grep size | sed 's/[^0-9]*//g'` + OFFSET=`pagestuff "$i" -p | tail -2 | grep offset | sed 's/[^0-9]*//g'` SIGNFILE="${TEMPDIR}/${TARGETFILE}.sign" - DIRNAME="`dirname ${SIGNFILE}`" + DIRNAME="`dirname "${SIGNFILE}"`" mkdir -p "${DIRNAME}" echo "Adding detached signature for: ${TARGETFILE}. Size: ${SIZE}. Offset: ${OFFSET}" - dd if=$i of=${SIGNFILE} bs=1 skip=${OFFSET} count=${SIZE} 2>/dev/null + dd if="$i" of="${SIGNFILE}" bs=1 skip=${OFFSET} count=${SIZE} 2>/dev/null done -for i in `grep CodeResources ${TEMPLIST}`; do - TARGETFILE="${BUNDLE}/`echo ${i} | sed "s|.*${BUNDLE}/||"`" +grep CodeResources < "${TEMPLIST}" | while read i; do + TARGETFILE="${BUNDLE}/`echo "${i}" | sed "s|.*${BUNDLE}/||"`" RESOURCE="${TEMPDIR}/${TARGETFILE}" DIRNAME="`dirname "${RESOURCE}"`" mkdir -p "${DIRNAME}" @@ -41,6 +41,6 @@ done rm ${TEMPLIST} -tar -C ${TEMPDIR} -czf ${OUT} . -rm -rf ${TEMPDIR} +tar -C "${TEMPDIR}" -czf "${OUT}" . +rm -rf "${TEMPDIR}" echo "Created ${OUT}" diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus index 0eb6b2c84d..2253c40af1 100755 --- a/contrib/macdeploy/macdeployqtplus +++ b/contrib/macdeploy/macdeployqtplus @@ -155,7 +155,7 @@ class FrameworkInfo(object): class ApplicationBundleInfo(object): def __init__(self, path): self.path = path - appName = os.path.splitext(os.path.basename(path))[0] + appName = "Bitcoin-Qt" self.binaryPath = os.path.join(path, "Contents", "MacOS", appName) if not os.path.exists(self.binaryPath): raise RuntimeError("Could not find bundle binary for " + path) @@ -596,7 +596,7 @@ if os.path.exists("dist"): # ------------------------------------------------ -target = os.path.join("dist", app_bundle) +target = os.path.join("dist", "Bitcoin-Qt.app") if verbose >= 2: print "+ Copying source bundle +" @@ -757,7 +757,7 @@ if config.dmg is not None: if fancy is None: try: - runHDIUtil("create", dmg_name, srcfolder="dist", format="UDBZ", volname=app_bundle_name, ov=True) + runHDIUtil("create", dmg_name, srcfolder="dist", format="UDBZ", volname="Bitcoin-Core", ov=True) except subprocess.CalledProcessError as e: sys.exit(e.returncode) else: @@ -772,7 +772,7 @@ if config.dmg is not None: if verbose >= 3: print "Creating temp image for modification..." try: - runHDIUtil("create", dmg_name + ".temp", srcfolder="dist", format="UDRW", size=size, volname=app_bundle_name, ov=True) + runHDIUtil("create", dmg_name + ".temp", srcfolder="dist", format="UDRW", size=size, volname="Bitcoin-Core", ov=True) except subprocess.CalledProcessError as e: sys.exit(e.returncode) @@ -837,7 +837,7 @@ if config.dmg is not None: items_positions.append(itemscript.substitute(params)) params = { - "disk" : "Bitcoin-Qt", + "disk" : "Bitcoin-Core", "window_bounds" : "300,300,800,620", "icon_size" : "96", "background_commands" : "", diff --git a/depends/Makefile b/depends/Makefile index 05ef33f2ee..ef5a20e6c3 100644 --- a/depends/Makefile +++ b/depends/Makefile @@ -91,12 +91,12 @@ include funcs.mk toolchain_path=$($($(host_arch)_$(host_os)_native_toolchain)_prefixbin) final_build_id_long+=$(shell $(build_SHA256SUM) config.site.in) final_build_id+=$(shell echo -n $(final_build_id_long) | $(build_SHA256SUM) | cut -c-$(HASH_LENGTH)) -$(host_prefix)/.stamp_$(final_build_id): | $(native_packages) $(packages) +$(host_prefix)/.stamp_$(final_build_id): $(native_packages) $(packages) $(AT)rm -rf $(@D) $(AT)mkdir -p $(@D) - $(AT)echo copying packages: $| + $(AT)echo copying packages: $^ $(AT)echo to: $(@D) - $(AT)cd $(@D); $(foreach package,$|, tar xf $($(package)_cached); ) + $(AT)cd $(@D); $(foreach package,$^, tar xf $($(package)_cached); ) $(AT)touch $@ $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_build_id) @@ -121,8 +121,35 @@ $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_ $< > $@ $(AT)touch $@ -install: $(host_prefix)/share/config.site -download-one: $(all_sources) + +define check_or_remove_cached + mkdir -p $(BASE_CACHE)/$(host)/$(package) && cd $(BASE_CACHE)/$(host)/$(package); \ + $(build_SHA256SUM) -c $($(package)_cached_checksum) >/dev/null 2>/dev/null || \ + ( rm -f $($(package)_cached_checksum); \ + if test -f "$($(package)_cached)"; then echo "Checksum mismatch for $(package). Forcing rebuild.."; rm -f $($(package)_cached_checksum) $($(package)_cached); fi ) +endef + +define check_or_remove_sources + mkdir -p $($(package)_source_dir); cd $($(package)_source_dir); \ + $(build_SHA256SUM) -c $($(package)_fetched) >/dev/null 2>/dev/null || \ + ( if test -f $($(package)_all_sources); then echo "Checksum missing or mismatched for $(package) source. Forcing re-download."; fi; \ + rm -f $($(package)_all_sources) $($(1)_fetched)) +endef + +check-packages: + @$(foreach package,$(all_packages),$(call check_or_remove_cached,$(package));) +check-sources: + @$(foreach package,$(all_packages),$(call check_or_remove_sources,$(package));) + +$(host_prefix)/share/config.site: check-packages + +check-packages: check-sources + +install: check-packages $(host_prefix)/share/config.site + + +download-one: check-sources $(all_sources) + download-osx: @$(MAKE) -s HOST=x86_64-apple-darwin11 download-one download-linux: @@ -130,4 +157,5 @@ download-linux: download-win: @$(MAKE) -s HOST=x86_64-w64-mingw32 download-one download: download-osx download-linux download-win -.PHONY: install cached download-one download-osx download-linux download-win download + +.PHONY: install cached download-one download-osx download-linux download-win download check-packages check-sources diff --git a/depends/README.md b/depends/README.md new file mode 100644 index 0000000000..2dc0b9e47e --- /dev/null +++ b/depends/README.md @@ -0,0 +1,56 @@ +### Usage + +To build dependencies for the current arch+OS: + + make + +To build for another arch/OS: + + make HOST=host-platform-triplet + +For example: + + make HOST=x86_64-w64-mingw32 -j4 + +A prefix will be generated that's suitable for plugging into Bitcoin's +configure. In the above example, a dir named i686-w64-mingw32 will be +created. To use it for Bitcoin: + + ./configure --prefix=`pwd`/depends/x86_64-w64-mingw32 + +Common `host-platform-triplets` for cross compilation are: + +- `i686-w64-mingw32` for Win32 +- `x86_64-w64-mingw32` for Win64 +- `x86_64-apple-darwin11` for MacOSX +- `arm-linux-gnueabihf` for Linux ARM + +No other options are needed, the paths are automatically configured. + +Dependency Options: +The following can be set when running make: make FOO=bar + + SOURCES_PATH: downloaded sources will be placed here + BASE_CACHE: built packages will be placed here + SDK_PATH: Path where sdk's can be found (used by OSX) + FALLBACK_DOWNLOAD_PATH: If a source file can't be fetched, try here before giving up + NO_QT: Don't download/build/cache qt and its dependencies + NO_WALLET: Don't download/build/cache libs needed to enable the wallet + NO_UPNP: Don't download/build/cache packages needed for enabling upnp + DEBUG: disable some optimizations and enable more runtime checking + +If some packages are not built, for example `make NO_WALLET=1`, the appropriate +options will be passed to bitcoin's configure. In this case, `--disable-wallet`. + +Additional targets: + + download: run 'make download' to fetch all sources without building them + download-osx: run 'make download-osx' to fetch all sources needed for osx builds + download-win: run 'make download-win' to fetch all sources needed for win builds + download-linux: run 'make download-linux' to fetch all sources needed for linux builds + +### Other documentation + +- [description.md](description.md): General description of the depends system +- [packages.md](packages.md): Steps for adding packages + diff --git a/depends/README.usage b/depends/README.usage deleted file mode 100644 index 24e1231d82..0000000000 --- a/depends/README.usage +++ /dev/null @@ -1,34 +0,0 @@ -To build dependencies for the current arch+OS: - make -To build for another arch/OS: - make HOST=host-platform-triplet && make HOST=host-platform-triplet - (For example: make HOST=i686-w64-mingw32 -j4) - -A prefix will be generated that's suitable for plugging into Bitcoin's -configure. In the above example, a dir named i686-w64-mingw32 will be -created. To use it for Bitcoin: - -./configure --prefix=`pwd`/depends/i686-w64-mingw32 - -No other options are needed, the paths are automatically configured. - -Dependency Options: -The following can be set when running make: make FOO=bar - -SOURCES_PATH: downloaded sources will be placed here -BASE_CACHE: built packages will be placed here -SDK_PATH: Path where sdk's can be found (used by OSX) -FALLBACK_DOWNLOAD_PATH: If a source file can't be fetched, try here before giving up -NO_QT: Don't download/build/cache qt and its dependencies -NO_WALLET: Don't download/build/cache libs needed to enable the wallet -NO_UPNP: Don't download/build/cache packages needed for enabling upnp -DEBUG: disable some optimizations and enable more runtime checking - -If some packages are not built, for example 'make NO_WALLET=1', the appropriate -options will be passed to bitcoin's configure. In this case, --disable-wallet. - -Additional targets: -download: run 'make download' to fetch all sources without building them -download-osx: run 'make download-osx' to fetch all sources needed for osx builds -download-win: run 'make download-win' to fetch all sources needed for win builds -download-linux: run 'make download-linux' to fetch all sources needed for linux builds diff --git a/depends/README b/depends/description.md index 55e7222697..74f9ef3f20 100644 --- a/depends/README +++ b/depends/description.md @@ -1,9 +1,7 @@ -This is a system of building and caching dependencies necessary for building -Bitcoin. - +This is a system of building and caching dependencies necessary for building Bitcoin. There are several features that make it different from most similar systems: -- It is designed to be builder and host agnostic +### It is designed to be builder and host agnostic In theory, binaries for any target OS/architecture can be created, from a builder running any OS/architecture. In practice, build-side tools must be @@ -11,18 +9,18 @@ specified when the defaults don't fit, and packages must be amended to work on new hosts. For now, a build architecture of x86_64 is assumed, either on Linux or OSX. -- No reliance on timestamps +### No reliance on timestamps File presence is used to determine what needs to be built. This makes the results distributable and easily digestable by automated builders. -- Each build only has its specified dependencies available at build-time. +### Each build only has its specified dependencies available at build-time. For each build, the sysroot is wiped and the (recursive) dependencies are installed. This makes each build deterministic, since there will never be any unknown files available to cause side-effects. -- Each package is cached and only rebuilt as needed. +### Each package is cached and only rebuilt as needed. Before building, a unique build-id is generated for each package. This id consists of a hash of all files used to build the package (Makefiles, packages, @@ -32,7 +30,7 @@ any other package that depends on it. If any of the main makefiles (Makefile, funcs.mk, etc) are changed, all packages will be rebuilt. After building, the results are cached into a tarball that can be re-used and distributed. -- Package build results are (relatively) deterministic. +### Package build results are (relatively) deterministic. Each package is configured and patched so that it will yield the same build-results with each consequent build, within a reasonable set of @@ -41,13 +39,13 @@ beyond the scope of this system. Additionally, the toolchain itself must be capable of deterministic results. When revisions are properly bumped, a cached build should represent an exact single payload. -- Sources are fetched and verified automatically +### Sources are fetched and verified automatically Each package must define its source location and checksum. The build will fail if the fetched source does not match. Sources may be pre-seeded and/or cached as desired. -- Self-cleaning +### Self-cleaning Build and staging dirs are wiped after use, and any previous version of a cached result is removed following a successful build. Automated builders diff --git a/depends/funcs.mk b/depends/funcs.mk index b407737f7f..050a9b1321 100644 --- a/depends/funcs.mk +++ b/depends/funcs.mk @@ -53,12 +53,14 @@ $(1)_staging_prefix_dir:=$$($(1)_staging_dir)$($($(1)_type)_prefix) $(1)_extract_dir:=$(base_build_dir)/$(host)/$(1)/$($(1)_version)-$($(1)_build_id) $(1)_download_dir:=$(base_download_dir)/$(1)-$($(1)_version) $(1)_build_dir:=$$($(1)_extract_dir)/$$($(1)_build_subdir) +$(1)_cached_checksum:=$(BASE_CACHE)/$(host)/$(1)/$(1)-$($(1)_version)-$($(1)_build_id).tar.gz.hash $(1)_patch_dir:=$(base_build_dir)/$(host)/$(1)/$($(1)_version)-$($(1)_build_id)/.patches-$($(1)_build_id) $(1)_prefixbin:=$($($(1)_type)_prefix)/bin/ $(1)_cached:=$(BASE_CACHE)/$(host)/$(1)/$(1)-$($(1)_version)-$($(1)_build_id).tar.gz +$(1)_all_sources=$($(1)_file_name) $($(1)_extra_sources) #stamps -$(1)_fetched=$$($(1)_source_dir)/download-stamps/.stamp_fetched-$(1)-$($(1)_file_name) +$(1)_fetched=$(SOURCES_PATH)/download-stamps/.stamp_fetched-$(1)-$($(1)_file_name).hash $(1)_extracted=$$($(1)_extract_dir)/.stamp_extracted $(1)_preprocessed=$$($(1)_extract_dir)/.stamp_preprocessed $(1)_cleaned=$$($(1)_extract_dir)/.stamp_cleaned @@ -154,7 +156,10 @@ endef define int_add_cmds $($(1)_fetched): $(AT)mkdir -p $$(@D) $(SOURCES_PATH) + $(AT)rm -f $$@ + $(AT)touch $$@ $(AT)cd $$(@D); $(call $(1)_fetch_cmds,$(1)) + $(AT)cd $($(1)_source_dir); $(foreach source,$($(1)_all_sources),$(build_SHA256SUM) $(source) >> $$(@);) $(AT)touch $$@ $($(1)_extracted): | $($(1)_fetched) $(AT)echo Extracting $(1)... @@ -195,10 +200,12 @@ $($(1)_cached): | $($(1)_dependencies) $($(1)_postprocessed) $(AT)rm -rf $$(@D) && mkdir -p $$(@D) $(AT)mv $$($(1)_staging_dir)/$$(@F) $$(@) $(AT)rm -rf $($(1)_staging_dir) +$($(1)_cached_checksum): $($(1)_cached) + $(AT)cd $$(@D); $(build_SHA256SUM) $$(<F) > $$(@) .PHONY: $(1) -$(1): | $($(1)_cached) -.SECONDARY: $($(1)_postprocessed) $($(1)_staged) $($(1)_built) $($(1)_configured) $($(1)_preprocessed) $($(1)_extracted) $($(1)_fetched) +$(1): | $($(1)_cached_checksum) +.SECONDARY: $($(1)_cached) $($(1)_postprocessed) $($(1)_staged) $($(1)_built) $($(1)_configured) $($(1)_preprocessed) $($(1)_extracted) $($(1)_fetched) endef diff --git a/depends/README.packages b/depends/packages.md index 5ab7ed7dee..7c80362509 100644 --- a/depends/README.packages +++ b/depends/packages.md @@ -4,119 +4,137 @@ variables, and defining build commands. The package "mylib" will be used here as an example General tips: -mylib_foo is written as $(package)_foo in order to make recipes more similar. +- mylib_foo is written as $(package)_foo in order to make recipes more similar. -Identifiers: +## Identifiers Each package is required to define at least these variables: - $(package)_version: + + $(package)_version: Version of the upstream library or program. If there is no version, a placeholder such as 1.0 can be used. - $(package)_download_path: + + $(package)_download_path: Location of the upstream source, without the file-name. Usually http or ftp. - $(package)_file_name: + + $(package)_file_name: The upstream source filename available at the download path. - $(package)_sha256_hash: + + $(package)_sha256_hash: The sha256 hash of the upstream file These variables are optional: - $(package)_build_subdir: + + $(package)_build_subdir: cd to this dir before running configure/build/stage commands. - $(package)_download_file: + + $(package)_download_file: The file-name of the upstream source if it differs from how it should be stored locally. This can be used to avoid storing file-names with strange characters. - $(package)_dependencies: + + $(package)_dependencies: Names of any other packages that this one depends on. - $(package)_patches: + + $(package)_patches: Filenames of any patches needed to build the package + $(package)_extra_sources: + Any extra files that will be fetched via $(package)_fetch_cmds. These are + specified so that they can be fetched and verified via 'make download'. + -Build Variables: +## Build Variables: After defining the main identifiers, build variables may be added or customized before running the build commands. They should be added to a function called $(package)_set_vars. For example: -define $(package)_set_vars -... -endef + define $(package)_set_vars + ... + endef Most variables can be prefixed with the host, architecture, or both, to make the modifications specific to that case. For example: - Universal: $(package)_cc=gcc - Linux only: $(package)_linux_cc=gcc - x86_64 only: $(package)_x86_64_cc = gcc - x86_64 linux only: $(package)_x86_64_linux_cc = gcc + Universal: $(package)_cc=gcc + Linux only: $(package)_linux_cc=gcc + x86_64 only: $(package)_x86_64_cc = gcc + x86_64 linux only: $(package)_x86_64_linux_cc = gcc These variables may be set to override or append their default values. - $(package)_cc - $(package)_cxx - $(package)_objc - $(package)_objcxx - $(package)_ar - $(package)_ranlib - $(package)_libtool - $(package)_nm - $(package)_cflags - $(package)_cxxflags - $(package)_ldflags - $(package)_cppflags - $(package)_config_env - $(package)_build_env - $(package)_stage_env - $(package)_build_opts - $(package)_config_opts + + $(package)_cc + $(package)_cxx + $(package)_objc + $(package)_objcxx + $(package)_ar + $(package)_ranlib + $(package)_libtool + $(package)_nm + $(package)_cflags + $(package)_cxxflags + $(package)_ldflags + $(package)_cppflags + $(package)_config_env + $(package)_build_env + $(package)_stage_env + $(package)_build_opts + $(package)_config_opts The *_env variables are used to add environment variables to the respective commands. Many variables respect a debug/release suffix as well, in order to use them for only the appropriate build config. For example: - $(package)_cflags_release = -O3 - $(package)_cflags_i686_debug = -g - $(package)_config_opts_release = --disable-debug + + $(package)_cflags_release = -O3 + $(package)_cflags_i686_debug = -g + $(package)_config_opts_release = --disable-debug These will be used in addition to the options that do not specify debug/release. All builds are considered to be release unless DEBUG=1 is set by -the user. +the user. Other variables may be defined as needed. -Other variables may be defined as needed. - -Build commands: +## Build commands: For each build, a unique build dir and staging dir are created. For example, - work/build/mylib/1.0-1adac830f6e and work/staging/mylib/1.0-1adac830f6e. + `work/build/mylib/1.0-1adac830f6e` and `work/staging/mylib/1.0-1adac830f6e`. The following build commands are available for each recipe: - $(package)_fetch_cmds: + $(package)_fetch_cmds: Runs from: build dir Fetch the source file. If undefined, it will be fetched and verified against its hash. - $(package)_extract_cmds: + + $(package)_extract_cmds: Runs from: build dir Verify the source file against its hash and extract it. If undefined, the source is assumed to be a tarball. - $(package)_preprocess_cmds: + + $(package)_preprocess_cmds: Runs from: build dir/$(package)_build_subdir Preprocess the source as necessary. If undefined, does nothing. - $(package)_config_cmds: + + $(package)_config_cmds: Runs from: build dir/$(package)_build_subdir Configure the source. If undefined, does nothing. - $(package)_build_cmds: + + $(package)_build_cmds: Runs from: build dir/$(package)_build_subdir Build the source. If undefined, does nothing. - $(package)_stage_cmds: + + $(package)_stage_cmds: Runs from: build dir/$(package)_build_subdir Stage the build results. If undefined, does nothing. The following variables are available for each recipe: - $(1)_staging_dir: package's destination sysroot path - $(1)_staging_prefix_dir: prefix path inside of the package's staging dir - $(1)_extract_dir: path to the package's extracted sources - $(1)_build_dir: path where configure/build/stage commands will be run - $(1)_patch_dir: path where the package's patches (if any) are found + + $(1)_staging_dir: package's destination sysroot path + $(1)_staging_prefix_dir: prefix path inside of the package's staging dir + $(1)_extract_dir: path to the package's extracted sources + $(1)_build_dir: path where configure/build/stage commands will be run + $(1)_patch_dir: path where the package's patches (if any) are found Notes on build commands: @@ -125,4 +143,5 @@ configure step to (usually) correctly configure automatically. Any $($(package)_config_opts) will be appended. Most autotools projects can be properly staged using: - $(MAKE) DESTDIR=$($(package)_staging_dir) install + + $(MAKE) DESTDIR=$($(package)_staging_dir) install diff --git a/depends/packages/native_cctools.mk b/depends/packages/native_cctools.mk index 951ad4fb29..1c1bcf199a 100644 --- a/depends/packages/native_cctools.mk +++ b/depends/packages/native_cctools.mk @@ -9,6 +9,8 @@ $(package)_clang_download_path=http://llvm.org/releases/$($(package)_clang_versi $(package)_clang_download_file=clang+llvm-$($(package)_clang_version)-amd64-Ubuntu-12.04.2.tar.gz $(package)_clang_file_name=clang-llvm-$($(package)_clang_version)-amd64-Ubuntu-12.04.2.tar.gz $(package)_clang_sha256_hash=60d8f69f032d62ef61bf527857ebb933741ec3352d4d328c5516aa520662dab7 +$(package)_extra_sources=$($(package)_clang_file_name) + define $(package)_fetch_cmds $(call fetch_file,$(package),$($(package)_download_path),$($(package)_download_file),$($(package)_file_name),$($(package)_sha256_hash)) && \ $(call fetch_file,$(package),$($(package)_clang_download_path),$($(package)_clang_download_file),$($(package)_clang_file_name),$($(package)_clang_sha256_hash)) diff --git a/doc/Doxyfile b/doc/Doxyfile index 8a11d1e8d0..925a33ee89 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -34,7 +34,7 @@ PROJECT_NAME = Bitcoin # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.10.99 +PROJECT_NUMBER = 0.11.99 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer diff --git a/doc/README.md b/doc/README.md index 0ade8f9923..ecddf50d4b 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,4 +1,4 @@ -Bitcoin Core 0.10.99 +Bitcoin Core 0.11.99 ===================== Setup @@ -43,6 +43,7 @@ The following are developer notes on how to build Bitcoin on your native platfor - [OSX Build Notes](build-osx.md) - [Unix Build Notes](build-unix.md) +- [Gitian Building Guide](gitian-building.md) Development --------------------- @@ -54,7 +55,11 @@ The Bitcoin repo's [root README](https://github.com/bitcoin/bitcoin/blob/master/ - [Release Process](release-process.md) - [Source Code Documentation (External Link)](https://dev.visucore.com/bitcoin/doxygen/) - [Translation Process](translation_process.md) +- [Translation Strings Policy](translation_strings_policy.md) - [Unit Tests](unit-tests.md) +- [Unauthenticated REST Interface](REST-interface.md) +- [BIPS](bips.md) +- [Dnsseed Policy](dnsseed-policy.md) ### Resources * Discuss on the [BitcoinTalk](https://bitcointalk.org/) forums, in the [Development & Technical Discussion board](https://bitcointalk.org/index.php?board=6.0). diff --git a/doc/README_osx.txt b/doc/README_osx.txt index e8af46e0e4..a572c7a241 100644 --- a/doc/README_osx.txt +++ b/doc/README_osx.txt @@ -30,7 +30,7 @@ originally done in toolchain4. To complicate things further, all builds must target an Apple SDK. These SDKs are free to download, but not redistributable. To obtain it, register for a developer account, then download the XCode 6.1.1 dmg: -https://developer.apple.com/downloads/download.action?path=Developer_Tools/xcode_6.1.1/xcode_6.1.1.dmg +https://developer.apple.com/devcenter/download.action?path=/Developer_Tools/xcode_6.1.1/xcode_6.1.1.dmg This file is several gigabytes in size, but only a single directory inside is needed: Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk diff --git a/doc/README_windows.txt b/doc/README_windows.txt index c582941696..e4fd9bdf90 100644 --- a/doc/README_windows.txt +++ b/doc/README_windows.txt @@ -1,4 +1,4 @@ -Bitcoin Core 0.10.99
+Bitcoin Core 0.11.99
=====================
Intro
diff --git a/doc/REST-interface.md b/doc/REST-interface.md index 11af040ad2..2219ceb65f 100644 --- a/doc/REST-interface.md +++ b/doc/REST-interface.md @@ -7,7 +7,7 @@ Supported API ------------- ####Transactions -`GET /rest/tx/TX-HASH.{bin|hex|json}` +`GET /rest/tx/<TX-HASH>.<bin|hex|json>` Given a transaction hash, Returns a transaction, in binary, hex-encoded binary or JSON formats. @@ -15,8 +15,8 @@ Returns a transaction, in binary, hex-encoded binary or JSON formats. For full TX query capability, one must enable the transaction index via "txindex=1" command line / configuration option. ####Blocks -`GET /rest/block/BLOCK-HASH.{bin|hex|json}` -`GET /rest/block/notxdetails/BLOCK-HASH.{bin|hex|json}` +`GET /rest/block/<BLOCK-HASH>.<bin|hex|json>` +`GET /rest/block/notxdetails/<BLOCK-HASH>.<bin|hex|json>` Given a block hash, Returns a block, in binary, hex-encoded binary or JSON formats. @@ -46,12 +46,38 @@ Only supports JSON as output format. * verificationprogress : (numeric) estimate of verification progress [0..1] * chainwork : (string) total amount of work in active chain, in hexadecimal -`GET /rest/getutxos` +####Query UTXO set +`GET /rest/getutxos/<checkmempool>/<txid>-<n>/<txid>-<n>/.../<txid>-<n>.<bin|hex|json>` -The getutxo command allows querying of the UTXO set given a set of of outpoints. +The getutxo command allows querying of the UTXO set given a set of outpoints. See BIP64 for input and output serialisation: https://github.com/bitcoin/bips/blob/master/bip-0064.mediawiki +Example: +``` +$ curl localhost:18332/rest/getutxos/checkmempool/b2cdfd7b89def827ff8af7cd9bff7627ff72e5e8b0f71210f92ea7a4000c5d75-0.json 2>/dev/null | json_pp +{ + "chaintipHash" : "00000000fb01a7f3745a717f8caebee056c484e6e0bfe4a9591c235bb70506fb", + "chainHeight" : 325347, + "utxos" : [ + { + "scriptPubKey" : { + "addresses" : [ + "mi7as51dvLJsizWnTMurtRmrP8hG2m1XvD" + ], + "type" : "pubkeyhash", + "hex" : "76a9141c7cebb529b86a04c683dfa87be49de35bcf589e88ac", + "reqSigs" : 1, + "asm" : "OP_DUP OP_HASH160 1c7cebb529b86a04c683dfa87be49de35bcf589e OP_EQUALVERIFY OP_CHECKSIG" + }, + "value" : 8.8687, + "height" : 2147483647, + "txvers" : 1 + } + ], + "bitmap" : "1" +} +``` Risks ------------- diff --git a/doc/assets-attribution.md b/doc/assets-attribution.md index c860cdc534..ebba64a61a 100644 --- a/doc/assets-attribution.md +++ b/doc/assets-attribution.md @@ -22,6 +22,7 @@ The following is a list of assets used in the bitcoin source and their proper at src/qt/res/icons/receive.png, src/qt/res/icons/remove.png, src/qt/res/icons/send.png, src/qt/res/icons/synced.png, src/qt/res/icons/transaction*.png, src/qt/res/icons/tx_output.png, + src/qt/res/icons/warning.png Jonas Schnelli ----------------------- diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 8f7db31d59..7d3d78adfc 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -102,7 +102,7 @@ CXXFLAGS="-g -ggdb -O0" or whatever debug flags you need. If the code is behaving strangely, take a look in the debug.log file in the data directory; error and debugging messages are written there. -The -debug=... command-line option controls debugging; running with just -debug will turn +The -debug=... command-line option controls debugging; running with just -debug or -debug=1 will turn on all categories (and give you a very large debug.log file). The Qt code routes qDebug() output to debug.log under category "qt": run with -debug=qt diff --git a/doc/release-notes.md b/doc/release-notes.md index 1cb517e5c7..6bb8587d78 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,3 +1,39 @@ (note: this is a temporary file, to be added-to by anybody, and moved to release-notes at release time) +Notable changes +=============== + +Example header +---------------------- + +Example content. + +0.12.0 Change log +================= + +Detailed release notes follow. This overview includes changes that affect +behavior, not code moves, refactors and string updates. For convenience in locating +the code changes and accompanying discussion, both the pull request and +git merge commit are mentioned. + +### RPC and REST + +### Configuration and command-line options + +### Block and transaction handling + +### P2P protocol and network code + +### Validation + +### Build system + +### Wallet + +### GUI + +### Tests + +### Miscellaneous + diff --git a/doc/release-notes/release-notes-0.10.1.md b/doc/release-notes/release-notes-0.10.1.md new file mode 100644 index 0000000000..5e939600a0 --- /dev/null +++ b/doc/release-notes/release-notes-0.10.1.md @@ -0,0 +1,143 @@ +Bitcoin Core version 0.10.1 is now available from: + + <https://bitcoin.org/bin/bitcoin-core-0.10.1/> + +This is a new minor version release, bringing bug fixes and translation +updates. It is recommended to upgrade to this version. + +Please report bugs using the issue tracker at github: + + <https://github.com/bitcoin/bitcoin/issues> + +Upgrading and downgrading +========================= + +How to Upgrade +-------------- + +If you are running an older version, shut it down. Wait until it has completely +shut down (which might take a few minutes for older versions), then run the +installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) or +bitcoind/bitcoin-qt (on Linux). + +Downgrade warning +------------------ + +Because release 0.10.0 and later makes use of headers-first synchronization and +parallel block download (see further), the block files and databases are not +backwards-compatible with pre-0.10 versions of Bitcoin Core or other software: + +* Blocks will be stored on disk out of order (in the order they are +received, really), which makes it incompatible with some tools or +other programs. Reindexing using earlier versions will also not work +anymore as a result of this. + +* The block index database will now hold headers for which no block is +stored on disk, which earlier versions won't support. + +If you want to be able to downgrade smoothly, make a backup of your entire data +directory. Without this your node will need start syncing (or importing from +bootstrap.dat) anew afterwards. It is possible that the data from a completely +synchronised 0.10 node may be usable in older versions as-is, but this is not +supported and may break as soon as the older version attempts to reindex. + +This does not affect wallet forward or backward compatibility. + +Notable changes +=============== + +This is a minor release and hence there are no notable changes. +For the notable changes in 0.10, refer to the release notes for the +0.10.0 release at https://github.com/bitcoin/bitcoin/blob/v0.10.0/doc/release-notes.md + +0.10.1 Change log +================= + +Detailed release notes follow. This overview includes changes that affect external +behavior, not code moves, refactors or string updates. + +RPC: +- `7f502be` fix crash: createmultisig and addmultisigaddress +- `eae305f` Fix missing lock in submitblock + +Block (database) and transaction handling: +- `1d2cdd2` Fix InvalidateBlock to add chainActive.Tip to setBlockIndexCandidates +- `c91c660` fix InvalidateBlock to repopulate setBlockIndexCandidates +- `002c8a2` fix possible block db breakage during re-index +- `a1f425b` Add (optional) consistency check for the block chain data structures +- `1c62e84` Keep mempool consistent during block-reorgs +- `57d1f46` Fix CheckBlockIndex for reindex +- `bac6fca` Set nSequenceId when a block is fully linked + +P2P protocol and network code: +- `78f64ef` don't trickle for whitelisted nodes +- `ca301bf` Reduce fingerprinting through timestamps in 'addr' messages. +- `200f293` Ignore getaddr messages on Outbound connections. +- `d5d8998` Limit message sizes before transfer +- `aeb9279` Better fingerprinting protection for non-main-chain getdatas. +- `cf0218f` Make addrman's bucket placement deterministic (countermeasure 1 against eclipse attacks, see http://cs-people.bu.edu/heilman/eclipse/) +- `0c6f334` Always use a 50% chance to choose between tried and new entries (countermeasure 2 against eclipse attacks) +- `214154e` Do not bias outgoing connections towards fresh addresses (countermeasure 2 against eclipse attacks) +- `aa587d4` Scale up addrman (countermeasure 6 against eclipse attacks) +- `139cd81` Cap nAttempts penalty at 8 and switch to pow instead of a division loop + +Validation: +- `d148f62` Acquire CCheckQueue's lock to avoid race condition + +Build system: +- `8752b5c` 0.10 fix for crashes on OSX 10.6 + +Wallet: +- N/A + +GUI: +- `2c08406` some mac specifiy cleanup (memory handling, unnecessary code) +- `81145a6` fix OSX dock icon window reopening +- `786cf72` fix a issue where "command line options"-action overwrite "Preference"-action (on OSX) + +Tests: +- `1117378` add RPC test for InvalidateBlock + +Miscellaneous: +- `c9e022b` Initialization: set Boost path locale in main thread +- `23126a0` Sanitize command strings before logging them. +- `323de27` Initialization: setup environment before starting QT tests +- `7494e09` Initialization: setup environment before starting tests +- `df45564` Initialization: set fallback locale as environment variable + +Credits +======= + +Thanks to everyone who directly contributed to this release: + +- Alex Morcos +- Cory Fields +- dexX7 +- fsb4000 +- Gavin Andresen +- Gregory Maxwell +- Ivan Pustogarov +- Jonas Schnelli +- Matt Corallo +- mrbandrews +- Pieter Wuille +- Ruben de Vries +- Suhas Daftuar +- Wladimir J. van der Laan + +And all those who contributed additional code review and/or security research: +- 21E14 +- Alison Kendler +- Aviv Zohar +- Ethan Heilman +- Evil-Knievel +- fanquake +- Jeff Garzik +- Jonas Nick +- Luke Dashjr +- Patrick Strateman +- Philip Kaufmann +- Sergio Demian Lerner +- Sharon Goldberg + +As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). diff --git a/doc/release-notes/release-notes-0.10.2.md b/doc/release-notes/release-notes-0.10.2.md new file mode 100644 index 0000000000..192ed69d29 --- /dev/null +++ b/doc/release-notes/release-notes-0.10.2.md @@ -0,0 +1,86 @@ +Bitcoin Core version 0.10.2 is now available from: + + <https://bitcoin.org/bin/bitcoin-core-0.10.2/> + +This is a new minor version release, bringing minor bug fixes and translation +updates. It is recommended to upgrade to this version. + +Please report bugs using the issue tracker at github: + + <https://github.com/bitcoin/bitcoin/issues> + +Upgrading and downgrading +========================= + +How to Upgrade +-------------- + +If you are running an older version, shut it down. Wait until it has completely +shut down (which might take a few minutes for older versions), then run the +installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) or +bitcoind/bitcoin-qt (on Linux). + +Downgrade warning +------------------ + +Because release 0.10.0 and later makes use of headers-first synchronization and +parallel block download (see further), the block files and databases are not +backwards-compatible with pre-0.10 versions of Bitcoin Core or other software: + +* Blocks will be stored on disk out of order (in the order they are +received, really), which makes it incompatible with some tools or +other programs. Reindexing using earlier versions will also not work +anymore as a result of this. + +* The block index database will now hold headers for which no block is +stored on disk, which earlier versions won't support. + +If you want to be able to downgrade smoothly, make a backup of your entire data +directory. Without this your node will need start syncing (or importing from +bootstrap.dat) anew afterwards. It is possible that the data from a completely +synchronised 0.10 node may be usable in older versions as-is, but this is not +supported and may break as soon as the older version attempts to reindex. + +This does not affect wallet forward or backward compatibility. + +Notable changes +=============== + +This fixes a serious problem on Windows with data directories that have non-ASCII +characters (https://github.com/bitcoin/bitcoin/issues/6078). + +For other platforms there are no notable changes. + +For the notable changes in 0.10, refer to the release notes +at https://github.com/bitcoin/bitcoin/blob/v0.10.0/doc/release-notes.md + +0.10.2 Change log +================= + +Detailed release notes follow. This overview includes changes that affect external +behavior, not code moves, refactors or string updates. + +Wallet: +- `824c011` fix boost::get usage with boost 1.58 + +Miscellaneous: +- `da65606` Avoid crash on start in TestBlockValidity with gen=1. +- `424ae66` don't imbue boost::filesystem::path with locale "C" on windows (fixes #6078) + +Credits +======= + +Thanks to everyone who directly contributed to this release: + +- Cory Fields +- Gregory Maxwell +- Jonas Schnelli +- Wladimir J. van der Laan + +And all those who contributed additional code review and/or security research: + +- dexX7 +- Pieter Wuille +- vayvanne + +As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). diff --git a/doc/release-notes/release-notes-0.9.4.md b/doc/release-notes/release-notes-0.9.4.md new file mode 100644 index 0000000000..7ee73246a9 --- /dev/null +++ b/doc/release-notes/release-notes-0.9.4.md @@ -0,0 +1,95 @@ +Bitcoin Core version 0.9.4 is now available from: + + https://bitcoin.org/bin/0.9.4/ + +This is a new minor version release, bringing only bug fixes and updated +translations. Upgrading to this release is recommended. + +Please report bugs using the issue tracker at github: + + https://github.com/bitcoin/bitcoin/issues + +How to Upgrade +=============== + +If you are running an older version, shut it down. Wait until it has completely +shut down (which might take a few minutes for older versions), then run the +installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) or +bitcoind/bitcoin-qt (on Linux). + +OpenSSL Warning +================ + +OpenSSL 1.0.0p / 1.0.1k was recently released and is being pushed out by +various operating system maintainers. Review by Gregory Maxwell determined that +this update is incompatible with the Bitcoin system and could lead to consensus +forks. + +Bitcoin Core released binaries from https://bitcoin.org are unaffected, +as are any built with the gitian deterministic build system. + +However, if you are running either + +- The Ubuntu PPA from https://launchpad.net/~bitcoin/+archive/ubuntu/bitcoin +- A third-party or self-compiled Bitcoin Core + +upgrade to Bitcoin Core 0.9.4, which includes a workaround, **before** updating +OpenSSL. + +The incompatibility is due to the OpenSSL update changing the +behavior of ECDSA validation to reject any signature which is +not encoded in a very rigid manner. This was a result of +OpenSSL's change for CVE-2014-8275 "Certificate fingerprints +can be modified". + +We are specifically aware of potential hard-forks due to signature +encoding handling and had been hoping to close them via BIP62 in 0.10. +BIP62's purpose is to improve transaction malleability handling and +as a side effect rigidly defines the encoding for signatures, but the +overall scope of BIP62 has made it take longer than we'd like to +deploy. + +0.9.4 changelog +================ + +Validation: +- `b8e81b7` consensus: guard against openssl's new strict DER checks +- `60c51f1` fail immediately on an empty signature +- `037bfef` Improve robustness of DER recoding code + +Command-line options: +- `cd5164a` Make -proxy set all network types, avoiding a connect leak. + +P2P: +- `bb424e4` Limit the number of new addressses to accumulate + +RPC: +- `0a94661` Disable SSLv3 (in favor of TLS) for the RPC client and server. + +Build system: +- `f047dfa` gitian: openssl-1.0.1i.tar.gz -> openssl-1.0.1k.tar.gz +- `5b9f78d` build: Fix OSX build when using Homebrew and qt5 +- `ffab1dd` Keep symlinks when copying into .app bundle +- `613247f` osx: fix signing to make Gatekeeper happy (again) + +Miscellaneous: +- `25b49b5` Refactor -alertnotify code +- `2743529` doc: Add instructions for consistent Mac OS X build names + +Credits +-------- + +Thanks to who contributed to this release, at least: + +- Cory Fields +- Gavin Andresen +- Gregory Maxwell +- Jeff Garzik +- Luke Dashjr +- Matt Corallo +- Pieter Wuille +- Saivann +- Sergio Demian Lerner +- Wladimir J. van der Laan + +As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). diff --git a/doc/release-notes/release-notes-0.9.5.md b/doc/release-notes/release-notes-0.9.5.md new file mode 100644 index 0000000000..bed0af9879 --- /dev/null +++ b/doc/release-notes/release-notes-0.9.5.md @@ -0,0 +1,60 @@ +Bitcoin Core version 0.9.5 is now available from: + + https://bitcoin.org/bin/0.9.5/ + +This is a new minor version release, with the goal of backporting BIP66. There +are also a few bug fixes and updated translations. Upgrading to this release is +recommended. + +Please report bugs using the issue tracker at github: + + https://github.com/bitcoin/bitcoin/issues + +How to Upgrade +=============== + +If you are running an older version, shut it down. Wait until it has completely +shut down (which might take a few minutes for older versions), then run the +installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) or +bitcoind/bitcoin-qt (on Linux). + +Notable changes +================ + +Mining and relay policy enhancements +------------------------------------ + +Bitcoin Core's block templates are now for version 3 blocks only, and any mining +software relying on its `getblocktemplate` must be updated in parallel to use +libblkmaker either version 0.4.2 or any version from 0.5.1 onward. +If you are solo mining, this will affect you the moment you upgrade Bitcoin +Core, which must be done prior to BIP66 achieving its 951/1001 status. +If you are mining with the stratum mining protocol: this does not affect you. +If you are mining with the getblocktemplate protocol to a pool: this will affect +you at the pool operator's discretion, which must be no later than BIP66 +achieving its 951/1001 status. + +0.9.5 changelog +================ + +- `74f29c2` Check pindexBestForkBase for null +- `9cd1dd9` Fix priority calculation in CreateTransaction +- `6b4163b` Sanitize command strings before logging them. +- `3230b32` Raise version of created blocks, and enforce DERSIG in mempool +- `989d499` Backport of some of BIP66's tests +- `ab03660` Implement BIP 66 validation rules and switchover logic +- `8438074` build: fix dynamic boost check when --with-boost= is used + +Credits +-------- + +Thanks to who contributed to this release, at least: + +- 21E14 +- Alex Morcos +- Cory Fields +- Gregory Maxwell +- Pieter Wuille +- Wladimir J. van der Laan + +As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). diff --git a/doc/release-process.md b/doc/release-process.md index cdcee0ec36..d3109c6aa2 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -44,7 +44,7 @@ Release Process Register and download the Apple SDK: (see OSX Readme for details) - https://developer.apple.com/downloads/download.action?path=Developer_Tools/xcode_6.1.1/xcode_6.1.1.dmg + https://developer.apple.com/devcenter/download.action?path=/Developer_Tools/xcode_6.1.1/xcode_6.1.1.dmg Using a Mac, create a tarball for the 10.9 SDK and copy it to the inputs directory: diff --git a/doc/translation_strings_policy.md b/doc/translation_strings_policy.md new file mode 100644 index 0000000000..6824b1d8ef --- /dev/null +++ b/doc/translation_strings_policy.md @@ -0,0 +1,72 @@ +Translation Strings Policy +=========================== + +This document provides guidelines for internationalization of the Bitcoin Core software. + +How to translate? +------------------ + +To mark a message as translatable + +- In GUI source code (under `src/qt`): use `tr("...")` + +- In non-GUI source code (under `src`): use `_("...")` + +No internationalization is used for e.g. developer scripts outside `src`. + +Strings to be translated +------------------------- + +On a high level, these strings are to be translated: + +- GUI strings, anything that appears in a dialog or window + +- Command-line option documentation + +### GUI strings + +Anything that appears to the user in the GUI is to be translated. This includes labels, menu items, button texts, tooltips and window titles. +This includes messages passed to the GUI through the UI interface through `InitMessage`, `ThreadSafeMessageBox` or `ShowProgress`. + +### Command-line options + +Documentation for the command line options in the output of `--help` should be translated as well. + +Make sure that default values do not end up in the string, but use string formatting like `strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), 100)`. Putting default values in strings has led to accidental translations in the past, and forces the string to be retranslated every time the value changes. + +Do not translate messages that are only shown to developers, such as those that only appear when `--help-debug` is used. + +General recommendations +------------------------ + +### Avoid unnecessary translation strings + +Try not to burden translators with translating messages that are e.g. slight variations of other messages. +In the GUI, avoid the use of text where an icon or symbol will do. +Make sure that placeholder texts in forms don't end up in the list of strings to be translated (use `<string notr="true">`). + +### Make translated strings understandable + +Try to write translation strings in an understandable way, for both the user and the translator. Avoid overly technical or detailed messages + +### Do not translate internal errors + +Do not translate internal errors, or log messages, or messages that appear on the RPC interface. If an error is to be shown to the user, +use a generic message, then log the detailed message to the log. E.g. "Error: A fatal internal error occurred, see debug.log for details". +This helps troubleshooting; if the error is the same for everyone, the likelihood is increased that it can be found using a search engine. + +### Avoid fragments + +Avoid dividing up a message into fragments. Translators see every string separately, so may misunderstand the context if the messages are not self-contained. + +### Avoid HTML in translation strings + +There have been difficulties with use of HTML in translation strings; translators should not be able to accidentally affect the formatting of messages. +This may sometimes be at conflict with the recommendation in the previous section. + +### String freezes + +During a string freeze (often before a major release), no translation strings are to be added, modified or removed. + +This can be checked by executing `make translate` in the `src` directory, then verifying that `bitcoin_en.ts` remains unchanged. + diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index ecde45059b..9b318650e9 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -31,18 +31,47 @@ testScripts=( 'proxy_test.py' 'merkle_blocks.py' 'signrawtransactions.py' + 'walletbackup.py' +); +testScriptsExt=( + 'bipdersig-p2p.py' + 'bipdersig.py' + 'getblocktemplate_longpoll.py' + 'getblocktemplate_proposals.py' + 'pruning.py' + 'forknotify.py' + 'invalidateblock.py' + 'keypool.py' + 'receivedby.py' + 'reindex.py' + 'rpcbind_test.py' +# 'script_test.py' + 'smartfees.py' 'maxblocksinflight.py' 'invalidblockrequest.py' 'rawtransactions.py' # 'forknotify.py' + 'p2p-acceptblock.py' ); + +extArg="-extended" +passOn=${@#$extArg} + if [ "x${ENABLE_BITCOIND}${ENABLE_UTILS}${ENABLE_WALLET}" = "x111" ]; then for (( i = 0; i < ${#testScripts[@]}; i++ )) do - if [ -z "$1" ] || [ "$1" == "${testScripts[$i]}" ] || [ "$1.py" == "${testScripts[$i]}" ] + if [ -z "$1" ] || [ "${1:0:1}" == "-" ] || [ "$1" == "${testScripts[$i]}" ] || [ "$1.py" == "${testScripts[$i]}" ] then echo -e "Running testscript \033[1m${testScripts[$i]}...\033[0m" - ${BUILDDIR}/qa/rpc-tests/${testScripts[$i]} --srcdir "${BUILDDIR}/src" + ${BUILDDIR}/qa/rpc-tests/${testScripts[$i]} --srcdir "${BUILDDIR}/src" ${passOn} + fi + done + for (( i = 0; i < ${#testScriptsExt[@]}; i++ )) + do + if [ "$1" == $extArg ] || [ "$1" == "${testScriptsExt[$i]}" ] || [ "$1.py" == "${testScriptsExt[$i]}" ] + then + echo -e "Running \033[1m2nd level\033[0m testscript \033[1m${testScriptsExt[$i]}...\033[0m" + ${BUILDDIR}/qa/rpc-tests/${testScriptsExt[$i]} --srcdir "${BUILDDIR}/src" ${passOn} fi done else diff --git a/qa/rpc-tests/README.md b/qa/rpc-tests/README.md index d9fbb109e8..6221c93d8b 100644 --- a/qa/rpc-tests/README.md +++ b/qa/rpc-tests/README.md @@ -6,25 +6,37 @@ Git subtree of [https://github.com/jgarzik/python-bitcoinrpc](https://github.com Changes to python-bitcoinrpc should be made upstream, and then pulled here using git subtree. -### [test_framework.py](test_framework.py) +### [test_framework/test_framework.py](test_framework/test_framework.py) Base class for new regression tests. -### [listtransactions.py](listtransactions.py) -Tests for the listtransactions RPC call. - -### [util.py](util.py) +### [test_framework/util.py](test_framework/util.py) Generally useful functions. Bash-based tests, to be ported to Python: ----------------------------------------- -- wallet.sh : Exercise wallet send/receive code. -- walletbackup.sh : Exercise wallet backup / dump / import -- txnmall.sh : Test proper accounting of malleable transactions - conflictedbalance.sh : More testing of malleable transaction handling Notes ===== +You can run a single test by calling `qa/pull-tester/rpc-tests.sh <testname>`. + +Run all possible tests with `qa/pull-tester/rpc-tests.sh -extended`. + +Possible options: + +```` +-h, --help show this help message and exit + --nocleanup Leave bitcoinds and test.* datadir on exit or error + --noshutdown Don't stop bitcoinds after the test execution + --srcdir=SRCDIR Source directory containing bitcoind/bitcoin-cli (default: + ../../src) + --tmpdir=TMPDIR Root directory for datadirs + --tracerpc Print out all RPC calls as they are made +``` + +If you set the environment variable `PYTHON_DEBUG=1` you will get some debug output (example: `PYTHON_DEBUG=1 qa/pull-tester/rpc-tests.sh wallet`). + A 200-block -regtest blockchain and wallets for four nodes is created the first time a regression test is run and is stored in the cache/ directory. Each node has 25 mature diff --git a/qa/rpc-tests/bipdersig-p2p.py b/qa/rpc-tests/bipdersig-p2p.py index ff0c878898..41717377b2 100755 --- a/qa/rpc-tests/bipdersig-p2p.py +++ b/qa/rpc-tests/bipdersig-p2p.py @@ -4,14 +4,14 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. # -from test_framework import ComparisonTestFramework -from util import * -from mininode import CTransaction, NetworkThread -from blocktools import create_coinbase, create_block +from test_framework.test_framework import ComparisonTestFramework +from test_framework.util import * +from test_framework.mininode import CTransaction, NetworkThread +from test_framework.blocktools import create_coinbase, create_block +from test_framework.comptool import TestInstance, TestManager +from test_framework.script import CScript from binascii import hexlify, unhexlify import cStringIO -from comptool import TestInstance, TestManager -from script import CScript import time # A canonical signature consists of: diff --git a/qa/rpc-tests/bipdersig.py b/qa/rpc-tests/bipdersig.py index 2c43bba865..243f816f65 100755 --- a/qa/rpc-tests/bipdersig.py +++ b/qa/rpc-tests/bipdersig.py @@ -7,9 +7,8 @@ # Test the BIP66 changeover logic # -from test_framework import BitcoinTestFramework -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * import os import shutil diff --git a/qa/rpc-tests/conflictedbalance.sh b/qa/rpc-tests/conflictedbalance.sh index 3b6c8dc318..7e44097374 100755 --- a/qa/rpc-tests/conflictedbalance.sh +++ b/qa/rpc-tests/conflictedbalance.sh @@ -49,7 +49,7 @@ B2ARGS="-datadir=$D2 -debug=mempool" $BITCOIND $B2ARGS & B2PID=$! -# Wait until all four nodes are at the same block number +# Wait until both nodes are at the same block number function WaitBlocks { while : do diff --git a/qa/rpc-tests/forknotify.py b/qa/rpc-tests/forknotify.py index af22ffb1a5..0acef8e30b 100755 --- a/qa/rpc-tests/forknotify.py +++ b/qa/rpc-tests/forknotify.py @@ -7,9 +7,8 @@ # Test -alertnotify # -from test_framework import BitcoinTestFramework -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * import os import shutil diff --git a/qa/rpc-tests/getblocktemplate_longpoll.py b/qa/rpc-tests/getblocktemplate_longpoll.py index 64fe49b835..aab4562422 100755 --- a/qa/rpc-tests/getblocktemplate_longpoll.py +++ b/qa/rpc-tests/getblocktemplate_longpoll.py @@ -3,9 +3,8 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -from test_framework import BitcoinTestFramework -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * def check_array_result(object_array, to_match, expected): diff --git a/qa/rpc-tests/getblocktemplate_proposals.py b/qa/rpc-tests/getblocktemplate_proposals.py index a63f456d6b..aca0cd7495 100755 --- a/qa/rpc-tests/getblocktemplate_proposals.py +++ b/qa/rpc-tests/getblocktemplate_proposals.py @@ -3,9 +3,8 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -from test_framework import BitcoinTestFramework -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * from binascii import a2b_hex, b2a_hex from hashlib import sha256 diff --git a/qa/rpc-tests/getchaintips.py b/qa/rpc-tests/getchaintips.py index 83a9537285..6a2bcb2969 100755 --- a/qa/rpc-tests/getchaintips.py +++ b/qa/rpc-tests/getchaintips.py @@ -7,8 +7,8 @@ # on chains of different lengths, and join the network together again. # This gives us two tips, verify that it works. -from test_framework import BitcoinTestFramework -from util import assert_equal +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal class GetChainTipsTest (BitcoinTestFramework): diff --git a/qa/rpc-tests/httpbasics.py b/qa/rpc-tests/httpbasics.py index 24533741e5..64ba49df64 100755 --- a/qa/rpc-tests/httpbasics.py +++ b/qa/rpc-tests/httpbasics.py @@ -7,8 +7,8 @@ # Test REST interface # -from test_framework import BitcoinTestFramework -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * import base64 try: diff --git a/qa/rpc-tests/invalidateblock.py b/qa/rpc-tests/invalidateblock.py index fd8a8e5785..2b9c8154e0 100755 --- a/qa/rpc-tests/invalidateblock.py +++ b/qa/rpc-tests/invalidateblock.py @@ -7,9 +7,8 @@ # Test InvalidateBlock code # -from test_framework import BitcoinTestFramework -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * class InvalidateTest(BitcoinTestFramework): diff --git a/qa/rpc-tests/invalidblockrequest.py b/qa/rpc-tests/invalidblockrequest.py index 8b685ed9b2..64b8e26395 100755 --- a/qa/rpc-tests/invalidblockrequest.py +++ b/qa/rpc-tests/invalidblockrequest.py @@ -4,11 +4,11 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. # -from test_framework import ComparisonTestFramework -from util import * -from comptool import TestManager, TestInstance -from mininode import * -from blocktools import * +from test_framework.test_framework import ComparisonTestFramework +from test_framework.util import * +from test_framework.comptool import TestManager, TestInstance +from test_framework.mininode import * +from test_framework.blocktools import * import logging import copy import time diff --git a/qa/rpc-tests/keypool.py b/qa/rpc-tests/keypool.py index 3840ea39d3..aee29a596a 100755 --- a/qa/rpc-tests/keypool.py +++ b/qa/rpc-tests/keypool.py @@ -8,7 +8,6 @@ # Add python-bitcoinrpc to module search path: import os import sys -sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "python-bitcoinrpc")) import json import shutil @@ -16,8 +15,7 @@ import subprocess import tempfile import traceback -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException -from util import * +from test_framework.util import * def check_array_result(object_array, to_match, expected): diff --git a/qa/rpc-tests/listtransactions.py b/qa/rpc-tests/listtransactions.py index 11e3635c04..eeae2d2fa2 100755 --- a/qa/rpc-tests/listtransactions.py +++ b/qa/rpc-tests/listtransactions.py @@ -5,9 +5,8 @@ # Exercise the listtransactions API -from test_framework import BitcoinTestFramework -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * def check_array_result(object_array, to_match, expected): diff --git a/qa/rpc-tests/maxblocksinflight.py b/qa/rpc-tests/maxblocksinflight.py index 87c80cd97e..a601147ce8 100755 --- a/qa/rpc-tests/maxblocksinflight.py +++ b/qa/rpc-tests/maxblocksinflight.py @@ -4,9 +4,9 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. # -from mininode import * -from test_framework import BitcoinTestFramework -from util import * +from test_framework.mininode import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * import logging ''' diff --git a/qa/rpc-tests/mempool_coinbase_spends.py b/qa/rpc-tests/mempool_coinbase_spends.py index 853d031de4..c64a15b9f5 100755 --- a/qa/rpc-tests/mempool_coinbase_spends.py +++ b/qa/rpc-tests/mempool_coinbase_spends.py @@ -8,9 +8,8 @@ # that spend (directly or indirectly) coinbase transactions. # -from test_framework import BitcoinTestFramework -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * import os import shutil diff --git a/qa/rpc-tests/mempool_resurrect_test.py b/qa/rpc-tests/mempool_resurrect_test.py index 6f7f577e36..19c74bb751 100755 --- a/qa/rpc-tests/mempool_resurrect_test.py +++ b/qa/rpc-tests/mempool_resurrect_test.py @@ -8,9 +8,8 @@ # the blockchain is re-organized. # -from test_framework import BitcoinTestFramework -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * import os import shutil @@ -34,7 +33,6 @@ class MempoolCoinbaseTest(BitcoinTestFramework): def run_test(self): node0_address = self.nodes[0].getnewaddress() - # Spend block 1/2/3's coinbase transactions # Mine a block. # Create three more transactions, spending the spends diff --git a/qa/rpc-tests/mempool_spendcoinbase.py b/qa/rpc-tests/mempool_spendcoinbase.py index ab5817c869..fc17c50692 100755 --- a/qa/rpc-tests/mempool_spendcoinbase.py +++ b/qa/rpc-tests/mempool_spendcoinbase.py @@ -13,9 +13,8 @@ # but less mature coinbase spends are NOT. # -from test_framework import BitcoinTestFramework -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * import os import shutil diff --git a/qa/rpc-tests/merkle_blocks.py b/qa/rpc-tests/merkle_blocks.py index a143d21a21..72a80ce6ca 100755 --- a/qa/rpc-tests/merkle_blocks.py +++ b/qa/rpc-tests/merkle_blocks.py @@ -7,9 +7,8 @@ # Test merkleblock fetch/validation # -from test_framework import BitcoinTestFramework -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * import os import shutil diff --git a/qa/rpc-tests/p2p-acceptblock.py b/qa/rpc-tests/p2p-acceptblock.py new file mode 100755 index 0000000000..fcdd1e1b99 --- /dev/null +++ b/qa/rpc-tests/p2p-acceptblock.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python2 +# +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# + +from test_framework.mininode import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +import time +from test_framework.blocktools import create_block, create_coinbase + +''' +AcceptBlockTest -- test processing of unrequested blocks. + +Since behavior differs when receiving unrequested blocks from whitelisted peers +versus non-whitelisted peers, this tests the behavior of both (effectively two +separate tests running in parallel). + +Setup: two nodes, node0 and node1, not connected to each other. Node0 does not +whitelist localhost, but node1 does. They will each be on their own chain for +this test. + +We have one NodeConn connection to each, test_node and white_node respectively. + +The test: +1. Generate one block on each node, to leave IBD. + +2. Mine a new block on each tip, and deliver to each node from node's peer. + The tip should advance. + +3. Mine a block that forks the previous block, and deliver to each node from + corresponding peer. + Node0 should not process this block (just accept the header), because it is + unrequested and doesn't have more work than the tip. + Node1 should process because this is coming from a whitelisted peer. + +4. Send another block that builds on the forking block. + Node0 should process this block but be stuck on the shorter chain, because + it's missing an intermediate block. + Node1 should reorg to this longer chain. + +5. Send a duplicate of the block in #3 to Node0. + Node0 should not process the block because it is unrequested, and stay on + the shorter chain. + +6. Send Node0 an inv for the height 3 block produced in #4 above. + Node0 should figure out that Node0 has the missing height 2 block and send a + getdata. + +7. Send Node0 the missing block again. + Node0 should process and the tip should advance. +''' + +# TestNode: bare-bones "peer". Used mostly as a conduit for a test to sending +# p2p messages to a node, generating the messages in the main testing logic. +class TestNode(NodeConnCB): + def __init__(self): + NodeConnCB.__init__(self) + self.create_callback_map() + self.connection = None + + def add_connection(self, conn): + self.connection = conn + + # Track the last getdata message we receive (used in the test) + def on_getdata(self, conn, message): + self.last_getdata = message + + # Spin until verack message is received from the node. + # We use this to signal that our test can begin. This + # is called from the testing thread, so it needs to acquire + # the global lock. + def wait_for_verack(self): + while True: + with mininode_lock: + if self.verack_received: + return + time.sleep(0.05) + + # Wrapper for the NodeConn's send_message function + def send_message(self, message): + self.connection.send_message(message) + +class AcceptBlockTest(BitcoinTestFramework): + def add_options(self, parser): + parser.add_option("--testbinary", dest="testbinary", + default=os.getenv("BITCOIND", "bitcoind"), + help="bitcoind binary to test") + + def setup_chain(self): + initialize_chain_clean(self.options.tmpdir, 2) + + def setup_network(self): + # Node0 will be used to test behavior of processing unrequested blocks + # from peers which are not whitelisted, while Node1 will be used for + # the whitelisted case. + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, ["-debug"], + binary=self.options.testbinary)) + self.nodes.append(start_node(1, self.options.tmpdir, + ["-debug", "-whitelist=127.0.0.1"], + binary=self.options.testbinary)) + + def run_test(self): + # Setup the p2p connections and start up the network thread. + test_node = TestNode() # connects to node0 (not whitelisted) + white_node = TestNode() # connects to node1 (whitelisted) + + connections = [] + connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node)) + connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], white_node)) + test_node.add_connection(connections[0]) + white_node.add_connection(connections[1]) + + NetworkThread().start() # Start up network handling in another thread + + # Test logic begins here + test_node.wait_for_verack() + white_node.wait_for_verack() + + # 1. Have both nodes mine a block (leave IBD) + [ n.generate(1) for n in self.nodes ] + tips = [ int ("0x" + n.getbestblockhash() + "L", 0) for n in self.nodes ] + + # 2. Send one block that builds on each tip. + # This should be accepted. + blocks_h2 = [] # the height 2 blocks on each node's chain + for i in xrange(2): + blocks_h2.append(create_block(tips[i], create_coinbase(), time.time()+1)) + blocks_h2[i].solve() + test_node.send_message(msg_block(blocks_h2[0])) + white_node.send_message(msg_block(blocks_h2[1])) + + time.sleep(1) + assert_equal(self.nodes[0].getblockcount(), 2) + assert_equal(self.nodes[1].getblockcount(), 2) + print "First height 2 block accepted by both nodes" + + # 3. Send another block that builds on the original tip. + blocks_h2f = [] # Blocks at height 2 that fork off the main chain + for i in xrange(2): + blocks_h2f.append(create_block(tips[i], create_coinbase(), blocks_h2[i].nTime+1)) + blocks_h2f[i].solve() + test_node.send_message(msg_block(blocks_h2f[0])) + white_node.send_message(msg_block(blocks_h2f[1])) + + time.sleep(1) # Give time to process the block + for x in self.nodes[0].getchaintips(): + if x['hash'] == blocks_h2f[0].hash: + assert_equal(x['status'], "headers-only") + + for x in self.nodes[1].getchaintips(): + if x['hash'] == blocks_h2f[1].hash: + assert_equal(x['status'], "valid-headers") + + print "Second height 2 block accepted only from whitelisted peer" + + # 4. Now send another block that builds on the forking chain. + blocks_h3 = [] + for i in xrange(2): + blocks_h3.append(create_block(blocks_h2f[i].sha256, create_coinbase(), blocks_h2f[i].nTime+1)) + blocks_h3[i].solve() + test_node.send_message(msg_block(blocks_h3[0])) + white_node.send_message(msg_block(blocks_h3[1])) + + time.sleep(1) + # Since the earlier block was not processed by node0, the new block + # can't be fully validated. + for x in self.nodes[0].getchaintips(): + if x['hash'] == blocks_h3[0].hash: + assert_equal(x['status'], "headers-only") + + # But this block should be accepted by node0 since it has more work. + try: + self.nodes[0].getblock(blocks_h3[0].hash) + print "Unrequested more-work block accepted from non-whitelisted peer" + except: + raise AssertionError("Unrequested more work block was not processed") + + # Node1 should have accepted and reorged. + assert_equal(self.nodes[1].getblockcount(), 3) + print "Successfully reorged to length 3 chain from whitelisted peer" + + # 5. Test handling of unrequested block on the node that didn't process + # Should still not be processed (even though it has a child that has more + # work). + test_node.send_message(msg_block(blocks_h2f[0])) + + # Here, if the sleep is too short, the test could falsely succeed (if the + # node hasn't processed the block by the time the sleep returns, and then + # the node processes it and incorrectly advances the tip). + # But this would be caught later on, when we verify that an inv triggers + # a getdata request for this block. + time.sleep(1) + assert_equal(self.nodes[0].getblockcount(), 2) + print "Unrequested block that would complete more-work chain was ignored" + + # 6. Try to get node to request the missing block. + # Poke the node with an inv for block at height 3 and see if that + # triggers a getdata on block 2 (it should if block 2 is missing). + with mininode_lock: + # Clear state so we can check the getdata request + test_node.last_getdata = None + test_node.send_message(msg_inv([CInv(2, blocks_h3[0].sha256)])) + + time.sleep(1) + with mininode_lock: + getdata = test_node.last_getdata + + # Check that the getdata is for the right block + assert_equal(len(getdata.inv), 1) + assert_equal(getdata.inv[0].hash, blocks_h2f[0].sha256) + print "Inv at tip triggered getdata for unprocessed block" + + # 7. Send the missing block for the third time (now it is requested) + test_node.send_message(msg_block(blocks_h2f[0])) + + time.sleep(1) + assert_equal(self.nodes[0].getblockcount(), 3) + print "Successfully reorged to length 3 chain from non-whitelisted peer" + + [ c.disconnect_node() for c in connections ] + +if __name__ == '__main__': + AcceptBlockTest().main() diff --git a/qa/rpc-tests/proxy_test.py b/qa/rpc-tests/proxy_test.py index d6d9e6725b..9a9b2f5300 100755 --- a/qa/rpc-tests/proxy_test.py +++ b/qa/rpc-tests/proxy_test.py @@ -7,9 +7,9 @@ import traceback, sys from binascii import hexlify import time, os -from socks5 import Socks5Configuration, Socks5Command, Socks5Server, AddressType -from test_framework import BitcoinTestFramework -from util import * +from test_framework.socks5 import Socks5Configuration, Socks5Command, Socks5Server, AddressType +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * ''' Test plan: - Start bitcoind's with different proxy configurations diff --git a/qa/rpc-tests/pruning.py b/qa/rpc-tests/pruning.py index f26cbee1e2..2824c51ce7 100755 --- a/qa/rpc-tests/pruning.py +++ b/qa/rpc-tests/pruning.py @@ -7,12 +7,12 @@ # Test pruning code # ******** # WARNING: -# This test uses 4GB of disk space and takes in excess of 30 mins to run +# This test uses 4GB of disk space. +# This test takes 30 mins or more (up to 2 hours) # ******** -from test_framework import BitcoinTestFramework -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * import os.path def calc_usage(blockdir): @@ -51,11 +51,11 @@ class PruneTest(BitcoinTestFramework): self.is_network_split = False # Create nodes 0 and 1 to mine - self.nodes.append(start_node(0, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-blockmaxsize=999000", "-checkblocks=5"], timewait=300)) - self.nodes.append(start_node(1, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-blockmaxsize=999000", "-checkblocks=5"], timewait=300)) + self.nodes.append(start_node(0, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-blockmaxsize=999000", "-checkblocks=5"], timewait=900)) + self.nodes.append(start_node(1, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-blockmaxsize=999000", "-checkblocks=5"], timewait=900)) # Create node 2 to test pruning - self.nodes.append(start_node(2, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-prune=550"], timewait=300)) + self.nodes.append(start_node(2, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-prune=550"], timewait=900)) self.prunedir = self.options.tmpdir+"/node2/regtest/blocks/" self.address[0] = self.nodes[0].getnewaddress() @@ -108,7 +108,7 @@ class PruneTest(BitcoinTestFramework): # Node 2 stays connected, so it hears about the stale blocks and then reorg's when node0 reconnects # Stopping node 0 also clears its mempool, so it doesn't have node1's transactions to accidentally mine stop_node(self.nodes[0],0) - self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-blockmaxsize=999000", "-checkblocks=5"], timewait=300) + self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-blockmaxsize=999000", "-checkblocks=5"], timewait=900) # Mine 24 blocks in node 1 self.utxo = self.nodes[1].listunspent() for i in xrange(24): @@ -135,7 +135,7 @@ class PruneTest(BitcoinTestFramework): # Reboot node 1 to clear its mempool (hopefully make the invalidate faster) # Lower the block max size so we don't keep mining all our big mempool transactions (from disconnected blocks) stop_node(self.nodes[1],1) - self.nodes[1]=start_node(1, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"], timewait=300) + self.nodes[1]=start_node(1, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"], timewait=900) height = self.nodes[1].getblockcount() print "Current block height:", height @@ -158,7 +158,7 @@ class PruneTest(BitcoinTestFramework): # Reboot node1 to clear those giant tx's from mempool stop_node(self.nodes[1],1) - self.nodes[1]=start_node(1, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"], timewait=300) + self.nodes[1]=start_node(1, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"], timewait=900) print "Generating new longer chain of 300 more blocks" self.nodes[1].generate(300) @@ -223,7 +223,7 @@ class PruneTest(BitcoinTestFramework): waitstart = time.time() while self.nodes[2].getblockcount() < goalbestheight: time.sleep(0.1) - if time.time() - waitstart > 300: + if time.time() - waitstart > 900: raise AssertionError("Node 2 didn't reorg to proper height") assert(self.nodes[2].getbestblockhash() == goalbesthash) # Verify we can now have the data for a block previously pruned @@ -256,7 +256,7 @@ class PruneTest(BitcoinTestFramework): def run_test(self): - print "Warning! This test requires 4GB of disk space and takes over 30 mins" + print "Warning! This test requires 4GB of disk space and takes over 30 mins (up to 2 hours)" print "Mining a big blockchain of 995 blocks" self.create_big_chain() # Chain diagram key: diff --git a/qa/rpc-tests/python-bitcoinrpc/bitcoinrpc/.gitignore b/qa/rpc-tests/python-bitcoinrpc/bitcoinrpc/.gitignore deleted file mode 100644 index 0d20b6487c..0000000000 --- a/qa/rpc-tests/python-bitcoinrpc/bitcoinrpc/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.pyc diff --git a/qa/rpc-tests/python-bitcoinrpc/setup.py b/qa/rpc-tests/python-bitcoinrpc/setup.py deleted file mode 100644 index 43cdb1c038..0000000000 --- a/qa/rpc-tests/python-bitcoinrpc/setup.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python2 - -from distutils.core import setup - -setup(name='python-bitcoinrpc', - version='0.1', - description='Enhanced version of python-jsonrpc for use with Bitcoin', - long_description=open('README').read(), - author='Jeff Garzik', - author_email='<jgarzik@exmulti.com>', - maintainer='Jeff Garzik', - maintainer_email='<jgarzik@exmulti.com>', - url='http://www.github.com/jgarzik/python-bitcoinrpc', - packages=['bitcoinrpc'], - classifiers=['License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', 'Operating System :: OS Independent']) diff --git a/qa/rpc-tests/rawtransactions.py b/qa/rpc-tests/rawtransactions.py index 3d80c97d74..1378514c84 100755 --- a/qa/rpc-tests/rawtransactions.py +++ b/qa/rpc-tests/rawtransactions.py @@ -8,8 +8,8 @@ # that spend (directly or indirectly) coinbase transactions. # -from test_framework import BitcoinTestFramework -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * from pprint import pprint from time import sleep diff --git a/qa/rpc-tests/receivedby.py b/qa/rpc-tests/receivedby.py index 1a681e1aae..16d6bd4cf1 100755 --- a/qa/rpc-tests/receivedby.py +++ b/qa/rpc-tests/receivedby.py @@ -5,9 +5,8 @@ # Exercise the listreceivedbyaddress API -from test_framework import BitcoinTestFramework -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * def get_sub_array_from_array(object_array, to_match): diff --git a/qa/rpc-tests/reindex.py b/qa/rpc-tests/reindex.py index fe767586bb..f2e3f248ea 100755 --- a/qa/rpc-tests/reindex.py +++ b/qa/rpc-tests/reindex.py @@ -6,9 +6,8 @@ # # Test -reindex with CheckBlockIndex # -from test_framework import BitcoinTestFramework -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * import os.path class ReindexTest(BitcoinTestFramework): diff --git a/qa/rpc-tests/rest.py b/qa/rpc-tests/rest.py index 9f0d049fe9..6c51b2fcd9 100755 --- a/qa/rpc-tests/rest.py +++ b/qa/rpc-tests/rest.py @@ -7,8 +7,9 @@ # Test REST interface # -from test_framework import BitcoinTestFramework -from util import * + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * from struct import * import binascii import json @@ -54,78 +55,78 @@ class RESTTest (BitcoinTestFramework): connect_nodes_bi(self.nodes,0,2) self.is_network_split=False self.sync_all() - + def run_test(self): url = urlparse.urlparse(self.nodes[0].url) print "Mining blocks..." - + self.nodes[0].generate(1) self.sync_all() self.nodes[2].generate(100) self.sync_all() - + assert_equal(self.nodes[0].getbalance(), 50) - + txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1) self.sync_all() self.nodes[2].generate(1) self.sync_all() bb_hash = self.nodes[0].getbestblockhash() - + assert_equal(self.nodes[1].getbalance(), Decimal("0.1")) #balance now should be 0.1 on node 1 - + # load the latest 0.1 tx over the REST API json_string = http_get_call(url.hostname, url.port, '/rest/tx/'+txid+self.FORMAT_SEPARATOR+"json") json_obj = json.loads(json_string) vintx = json_obj['vin'][0]['txid'] # get the vin to later check for utxo (should be spent by then) - # get n of 0.1 outpoint + # get n of 0.1 outpoint n = 0 for vout in json_obj['vout']: if vout['value'] == 0.1: n = vout['n'] - - + + ###################################### # GETUTXOS: query a unspent outpoint # ###################################### - json_request = '{"checkmempool":true,"outpoints":[{"txid":"'+txid+'","n":'+str(n)+'}]}' - json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'json', json_request) + json_request = '/checkmempool/'+txid+'-'+str(n) + json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json') json_obj = json.loads(json_string) - + #check chainTip response assert_equal(json_obj['chaintipHash'], bb_hash) - + #make sure there is one utxo assert_equal(len(json_obj['utxos']), 1) assert_equal(json_obj['utxos'][0]['value'], 0.1) - - + + ################################################ # GETUTXOS: now query a already spent outpoint # ################################################ - json_request = '{"checkmempool":true,"outpoints":[{"txid":"'+vintx+'","n":0}]}' - json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'json', json_request) + json_request = '/checkmempool/'+vintx+'-0' + json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json') json_obj = json.loads(json_string) - + #check chainTip response assert_equal(json_obj['chaintipHash'], bb_hash) #make sure there is no utox in the response because this oupoint has been spent assert_equal(len(json_obj['utxos']), 0) - + #check bitmap assert_equal(json_obj['bitmap'], "0") - - + + ################################################## # GETUTXOS: now check both with the same request # ################################################## - json_request = '{"checkmempool":true,"outpoints":[{"txid":"'+txid+'","n":'+str(n)+'},{"txid":"'+vintx+'","n":0}]}' - json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'json', json_request) + json_request = '/checkmempool/'+txid+'-'+str(n)+'/'+vintx+'-0' + json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json') json_obj = json.loads(json_string) assert_equal(len(json_obj['utxos']), 1) assert_equal(json_obj['bitmap'], "10") - + #test binary response bb_hash = self.nodes[0].getbestblockhash() @@ -134,19 +135,18 @@ class RESTTest (BitcoinTestFramework): binaryRequest += pack("i", n); binaryRequest += binascii.unhexlify(vintx); binaryRequest += pack("i", 0); - + bin_response = http_get_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'bin', binaryRequest) - output = StringIO.StringIO() output.write(bin_response) output.seek(0) chainHeight = unpack("i", output.read(4))[0] hashFromBinResponse = hex(deser_uint256(output))[2:].zfill(65).rstrip("L") - + assert_equal(bb_hash, hashFromBinResponse) #check if getutxo's chaintip during calculation was fine assert_equal(chainHeight, 102) #chain height must be 102 - - + + ############################ # GETUTXOS: mempool checks # ############################ @@ -156,55 +156,56 @@ class RESTTest (BitcoinTestFramework): json_string = http_get_call(url.hostname, url.port, '/rest/tx/'+txid+self.FORMAT_SEPARATOR+"json") json_obj = json.loads(json_string) vintx = json_obj['vin'][0]['txid'] # get the vin to later check for utxo (should be spent by then) - # get n of 0.1 outpoint + # get n of 0.1 outpoint n = 0 for vout in json_obj['vout']: if vout['value'] == 0.1: n = vout['n'] - - json_request = '{"checkmempool":false,"outpoints":[{"txid":"'+txid+'","n":'+str(n)+'}]}' - json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'json', json_request) + + json_request = '/'+txid+'-'+str(n) + json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json') json_obj = json.loads(json_string) assert_equal(len(json_obj['utxos']), 0) #there should be a outpoint because it has just added to the mempool - - json_request = '{"checkmempool":true,"outpoints":[{"txid":"'+txid+'","n":'+str(n)+'}]}' - json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'json', json_request) + + json_request = '/checkmempool/'+txid+'-'+str(n) + json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json') json_obj = json.loads(json_string) assert_equal(len(json_obj['utxos']), 1) #there should be a outpoint because it has just added to the mempool - + #do some invalid requests json_request = '{"checkmempool' response = http_get_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'json', json_request, True) assert_equal(response.status, 500) #must be a 500 because we send a invalid json request - + json_request = '{"checkmempool' response = http_get_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'bin', json_request, True) assert_equal(response.status, 500) #must be a 500 because we send a invalid bin request - + + response = http_get_call(url.hostname, url.port, '/rest/getutxos/checkmempool'+self.FORMAT_SEPARATOR+'bin', '', True) + assert_equal(response.status, 500) #must be a 500 because we send a invalid bin request + #test limits - json_request = '{"checkmempool":true,"outpoints":[' - for x in range(0, 200): - json_request += '{"txid":"'+txid+'","n":'+str(n)+'},' - json_request = json_request.rstrip(",") - json_request+="]}"; - response = http_get_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'json', json_request, True) + json_request = '/checkmempool/' + for x in range(0, 20): + json_request += txid+'-'+str(n)+'/' + json_request = json_request.rstrip("/") + response = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json', '', True) assert_equal(response.status, 500) #must be a 500 because we exceeding the limits - - json_request = '{"checkmempool":true,"outpoints":[' - for x in range(0, 90): - json_request += '{"txid":"'+txid+'","n":'+str(n)+'},' - json_request = json_request.rstrip(",") - json_request+="]}"; - response = http_get_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'json', json_request, True) + + json_request = '/checkmempool/' + for x in range(0, 15): + json_request += txid+'-'+str(n)+'/' + json_request = json_request.rstrip("/"); + response = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json', '', True) assert_equal(response.status, 200) #must be a 500 because we exceeding the limits self.nodes[0].generate(1) #generate block to not affect upcomming tests self.sync_all() - + ################ # /rest/block/ # ################ - + # check binary format response = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+"bin", "", True) assert_equal(response.status, 200) @@ -248,7 +249,7 @@ class RESTTest (BitcoinTestFramework): hex_string = http_get_call(url.hostname, url.port, '/rest/tx/'+tx_hash+self.FORMAT_SEPARATOR+"hex", "", True) assert_equal(hex_string.status, 200) assert_greater_than(int(response.getheader('content-length')), 10) - + # check block tx details @@ -278,7 +279,7 @@ class RESTTest (BitcoinTestFramework): #test rest bestblock bb_hash = self.nodes[0].getbestblockhash() - + json_string = http_get_call(url.hostname, url.port, '/rest/chaininfo.json') json_obj = json.loads(json_string) assert_equal(json_obj['bestblockhash'], bb_hash) diff --git a/qa/rpc-tests/rpcbind_test.py b/qa/rpc-tests/rpcbind_test.py index 655e00b6e7..04110c2831 100755 --- a/qa/rpc-tests/rpcbind_test.py +++ b/qa/rpc-tests/rpcbind_test.py @@ -8,7 +8,6 @@ # Add python-bitcoinrpc to module search path: import os import sys -sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "python-bitcoinrpc")) import json import shutil @@ -16,9 +15,8 @@ import subprocess import tempfile import traceback -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException -from util import * -from netutil import * +from test_framework.util import * +from test_framework.netutil import * def run_bind_test(tmpdir, allow_ips, connect_to, addresses, expected): ''' diff --git a/qa/rpc-tests/script_test.py b/qa/rpc-tests/script_test.py index 1ba3a478a8..860fa56b64 100755 --- a/qa/rpc-tests/script_test.py +++ b/qa/rpc-tests/script_test.py @@ -19,12 +19,12 @@ that flag, we use a block time before the switchover date). NOTE: This test is very slow and may take more than 40 minutes to run. ''' -from test_framework import ComparisonTestFramework -from util import * -from comptool import TestInstance, TestManager -from mininode import * -from blocktools import * -from script import * +from test_framework.test_framework import ComparisonTestFramework +from test_framework.util import * +from test_framework.comptool import TestInstance, TestManager +from test_framework.mininode import * +from test_framework.blocktools import * +from test_framework.script import * import logging import copy import json @@ -42,7 +42,7 @@ class ScriptTestFile(object): def load_files(self): for f in self.files: - self.data.extend(json.loads(open(f).read())) + self.data.extend(json.loads(open(os.path.dirname(os.path.abspath(__file__))+"/"+f).read())) # Skip over records that are not long enough to be tests def get_records(self): diff --git a/qa/rpc-tests/signrawtransactions.py b/qa/rpc-tests/signrawtransactions.py index 943634bd19..d51d6ee610 100755 --- a/qa/rpc-tests/signrawtransactions.py +++ b/qa/rpc-tests/signrawtransactions.py @@ -3,8 +3,8 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -from test_framework import BitcoinTestFramework -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * class SignRawTransactionsTest(BitcoinTestFramework): diff --git a/qa/rpc-tests/smartfees.py b/qa/rpc-tests/smartfees.py index 4eb8bb4842..c15c5fda09 100755 --- a/qa/rpc-tests/smartfees.py +++ b/qa/rpc-tests/smartfees.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -7,86 +7,252 @@ # Test fee estimation code # -from test_framework import BitcoinTestFramework -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + +# Construct 2 trivial P2SH's and the ScriptSigs that spend them +# So we can create many many transactions without needing to spend +# time signing. +P2SH_1 = "2MySexEGVzZpRgNQ1JdjdP5bRETznm3roQ2" # P2SH of "OP_1 OP_DROP" +P2SH_2 = "2NBdpwq8Aoo1EEKEXPNrKvr5xQr3M9UfcZA" # P2SH of "OP_2 OP_DROP" +# Associated ScriptSig's to spend satisfy P2SH_1 and P2SH_2 +# 4 bytes of OP_TRUE and push 2-byte redeem script of "OP_1 OP_DROP" or "OP_2 OP_DROP" +SCRIPT_SIG = ["0451025175", "0451025275"] + +def satoshi_round(amount): + return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN) + +def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee_increment): + ''' + Create and send a transaction with a random fee. + The transaction pays to a trival P2SH script, and assumes that its inputs + are of the same form. + The function takes a list of confirmed outputs and unconfirmed outputs + and attempts to use the confirmed list first for its inputs. + It adds the newly created outputs to the unconfirmed list. + Returns (raw transaction, fee) + ''' + # It's best to exponentially distribute our random fees + # because the buckets are exponentially spaced. + # Exponentially distributed from 1-128 * fee_increment + rand_fee = float(fee_increment)*(1.1892**random.randint(0,28)) + # Total fee ranges from min_fee to min_fee + 127*fee_increment + fee = min_fee - fee_increment + satoshi_round(rand_fee) + inputs = [] + total_in = Decimal("0.00000000") + while total_in <= (amount + fee) and len(conflist) > 0: + t = conflist.pop(0) + total_in += t["amount"] + inputs.append({ "txid" : t["txid"], "vout" : t["vout"]} ) + if total_in <= amount + fee: + while total_in <= (amount + fee) and len(unconflist) > 0: + t = unconflist.pop(0) + total_in += t["amount"] + inputs.append({ "txid" : t["txid"], "vout" : t["vout"]} ) + if total_in <= amount + fee: + raise RuntimeError("Insufficient funds: need %d, have %d"%(amount+fee, total_in)) + outputs = {} + outputs[P2SH_1] = total_in - amount - fee + outputs[P2SH_2] = amount + rawtx = from_node.createrawtransaction(inputs, outputs) + # Createrawtransaction constructions a transaction that is ready to be signed + # These transactions don't need to be signed, but we still have to insert the ScriptSig + # that will satisfy the ScriptPubKey. + completetx = rawtx[0:10] + inputnum = 0 + for inp in inputs: + completetx += rawtx[10+82*inputnum:82+82*inputnum] + completetx += SCRIPT_SIG[inp["vout"]] + completetx += rawtx[84+82*inputnum:92+82*inputnum] + inputnum += 1 + completetx += rawtx[10+82*inputnum:] + txid = from_node.sendrawtransaction(completetx, True) + unconflist.append({ "txid" : txid, "vout" : 0 , "amount" : total_in - amount - fee}) + unconflist.append({ "txid" : txid, "vout" : 1 , "amount" : amount}) + + return (completetx, fee) + +def split_inputs(from_node, txins, txouts, initial_split = False): + ''' + We need to generate a lot of very small inputs so we can generate a ton of transactions + and they will have low priority. + This function takes an input from txins, and creates and sends a transaction + which splits the value into 2 outputs which are appended to txouts. + ''' + prevtxout = txins.pop() + inputs = [] + outputs = {} + inputs.append({ "txid" : prevtxout["txid"], "vout" : prevtxout["vout"] }) + half_change = satoshi_round(prevtxout["amount"]/2) + rem_change = prevtxout["amount"] - half_change - Decimal("0.00001000") + outputs[P2SH_1] = half_change + outputs[P2SH_2] = rem_change + rawtx = from_node.createrawtransaction(inputs, outputs) + # If this is the initial split we actually need to sign the transaction + # Otherwise we just need to insert the property ScriptSig + if (initial_split) : + completetx = from_node.signrawtransaction(rawtx)["hex"] + else : + completetx = rawtx[0:82] + SCRIPT_SIG[prevtxout["vout"]] + rawtx[84:] + txid = from_node.sendrawtransaction(completetx, True) + txouts.append({ "txid" : txid, "vout" : 0 , "amount" : half_change}) + txouts.append({ "txid" : txid, "vout" : 1 , "amount" : rem_change}) + +def check_estimates(node, fees_seen, max_invalid, print_estimates = True): + ''' + This function calls estimatefee and verifies that the estimates + meet certain invariants. + ''' + all_estimates = [ node.estimatefee(i) for i in range(1,26) ] + if print_estimates: + print([str(all_estimates[e-1]) for e in [1,2,3,6,15,25]]) + delta = 1.0e-6 # account for rounding error + last_e = max(fees_seen) + for e in filter(lambda x: x >= 0, all_estimates): + # Estimates should be within the bounds of what transactions fees actually were: + if float(e)+delta < min(fees_seen) or float(e)-delta > max(fees_seen): + raise AssertionError("Estimated fee (%f) out of range (%f,%f)" + %(float(e), min(fees_seen), max(fees_seen))) + # Estimates should be monotonically decreasing + if float(e)-delta > last_e: + raise AssertionError("Estimated fee (%f) larger than last fee (%f) for lower number of confirms" + %(float(e),float(last_e))) + last_e = e + valid_estimate = False + invalid_estimates = 0 + for e in all_estimates: + if e >= 0: + valid_estimate = True + else: + invalid_estimates += 1 + # Once we're at a high enough confirmation count that we can give an estimate + # We should have estimates for all higher confirmation counts + if valid_estimate and e < 0: + raise AssertionError("Invalid estimate appears at higher confirm count than valid estimate") + # Check on the expected number of different confirmation counts + # that we might not have valid estimates for + if invalid_estimates > max_invalid: + raise AssertionError("More than (%d) invalid estimates"%(max_invalid)) + return all_estimates + class EstimateFeeTest(BitcoinTestFramework): def setup_network(self): + ''' + We'll setup the network to have 3 nodes that all mine with different parameters. + But first we need to use one node to create a lot of small low priority outputs + which we will use to generate our transactions. + ''' self.nodes = [] - self.nodes.append(start_node(0, self.options.tmpdir, - ["-debug=mempool", "-debug=estimatefee", "-relaypriority=0"])) - # Node1 mines small-but-not-tiny blocks, and allows free transactions. + # Use node0 to mine blocks for input splitting + self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000", + "-relaypriority=0", "-whitelist=127.0.0.1"])) + + print("This test is time consuming, please be patient") + print("Splitting inputs to small size so we can generate low priority tx's") + self.txouts = [] + self.txouts2 = [] + # Split a coinbase into two transaction puzzle outputs + split_inputs(self.nodes[0], self.nodes[0].listunspent(0), self.txouts, True) + + # Mine + while (len(self.nodes[0].getrawmempool()) > 0): + self.nodes[0].generate(1) + + # Repeatedly split those 2 outputs, doubling twice for each rep + # Use txouts to monitor the available utxo, since these won't be tracked in wallet + reps = 0 + while (reps < 5): + #Double txouts to txouts2 + while (len(self.txouts)>0): + split_inputs(self.nodes[0], self.txouts, self.txouts2) + while (len(self.nodes[0].getrawmempool()) > 0): + self.nodes[0].generate(1) + #Double txouts2 to txouts + while (len(self.txouts2)>0): + split_inputs(self.nodes[0], self.txouts2, self.txouts) + while (len(self.nodes[0].getrawmempool()) > 0): + self.nodes[0].generate(1) + reps += 1 + print("Finished splitting") + + # Now we can connect the other nodes, didn't want to connect them earlier + # so the estimates would not be affected by the splitting transactions + # Node1 mines small blocks but that are bigger than the expected transaction rate, + # and allows free transactions. # NOTE: the CreateNewBlock code starts counting block size at 1,000 bytes, - # so blockmaxsize of 2,000 is really just 1,000 bytes (room enough for - # 6 or 7 transactions) + # (17k is room enough for 110 or so transactions) self.nodes.append(start_node(1, self.options.tmpdir, - ["-blockprioritysize=1500", "-blockmaxsize=2000", - "-debug=mempool", "-debug=estimatefee", "-relaypriority=0"])) + ["-blockprioritysize=1500", "-blockmaxsize=18000", + "-maxorphantx=1000", "-relaypriority=0", "-debug=estimatefee"])) connect_nodes(self.nodes[1], 0) # Node2 is a stingy miner, that - # produces very small blocks (room for only 3 or so transactions) - node2args = [ "-blockprioritysize=0", "-blockmaxsize=1500", - "-debug=mempool", "-debug=estimatefee", "-relaypriority=0"] + # produces too small blocks (room for only 70 or so transactions) + node2args = ["-blockprioritysize=0", "-blockmaxsize=12000", "-maxorphantx=1000", "-relaypriority=0"] + self.nodes.append(start_node(2, self.options.tmpdir, node2args)) - connect_nodes(self.nodes[2], 0) + connect_nodes(self.nodes[0], 2) + connect_nodes(self.nodes[2], 1) self.is_network_split = False self.sync_all() - + + def transact_and_mine(self, numblocks, mining_node): + min_fee = Decimal("0.00001") + # We will now mine numblocks blocks generating on average 100 transactions between each block + # We shuffle our confirmed txout set before each set of transactions + # small_txpuzzle_randfee will use the transactions that have inputs already in the chain when possible + # resorting to tx's that depend on the mempool when those run out + for i in range(numblocks): + random.shuffle(self.confutxo) + for j in range(random.randrange(100-50,100+50)): + from_index = random.randint(1,2) + (txhex, fee) = small_txpuzzle_randfee(self.nodes[from_index], self.confutxo, + self.memutxo, Decimal("0.005"), min_fee, min_fee) + tx_kbytes = (len(txhex)/2)/1000.0 + self.fees_per_kb.append(float(fee)/tx_kbytes) + sync_mempools(self.nodes[0:3],.1) + mined = mining_node.getblock(mining_node.generate(1)[0],True)["tx"] + sync_blocks(self.nodes[0:3],.1) + #update which txouts are confirmed + newmem = [] + for utx in self.memutxo: + if utx["txid"] in mined: + self.confutxo.append(utx) + else: + newmem.append(utx) + self.memutxo = newmem def run_test(self): - # Prime the memory pool with pairs of transactions - # (high-priority, random fee and zero-priority, random fee) - min_fee = Decimal("0.001") - fees_per_kb = []; - for i in range(12): - (txid, txhex, fee) = random_zeropri_transaction(self.nodes, Decimal("1.1"), - min_fee, min_fee, 20) - tx_kbytes = (len(txhex)/2)/1000.0 - fees_per_kb.append(float(fee)/tx_kbytes) - - # Mine blocks with node2 until the memory pool clears: - count_start = self.nodes[2].getblockcount() - while len(self.nodes[2].getrawmempool()) > 0: - self.nodes[2].generate(1) - self.sync_all() - - all_estimates = [ self.nodes[0].estimatefee(i) for i in range(1,20) ] - print("Fee estimates, super-stingy miner: "+str([str(e) for e in all_estimates])) + self.fees_per_kb = [] + self.memutxo = [] + self.confutxo = self.txouts # Start with the set of confirmed txouts after splitting + print("Checking estimates for 1/2/3/6/15/25 blocks") + print("Creating transactions and mining them with a huge block size") + # Create transactions and mine 20 big blocks with node 0 such that the mempool is always emptied + self.transact_and_mine(30, self.nodes[0]) + check_estimates(self.nodes[1], self.fees_per_kb, 1) - # Estimates should be within the bounds of what transactions fees actually were: - delta = 1.0e-6 # account for rounding error - for e in filter(lambda x: x >= 0, all_estimates): - if float(e)+delta < min(fees_per_kb) or float(e)-delta > max(fees_per_kb): - raise AssertionError("Estimated fee (%f) out of range (%f,%f)"%(float(e), min_fee_kb, max_fee_kb)) - - # Generate transactions while mining 30 more blocks, this time with node1: - for i in range(30): - for j in range(random.randrange(6-4,6+4)): - (txid, txhex, fee) = random_transaction(self.nodes, Decimal("1.1"), - Decimal("0.0"), min_fee, 20) - tx_kbytes = (len(txhex)/2)/1000.0 - fees_per_kb.append(float(fee)/tx_kbytes) - self.nodes[1].generate(1) - self.sync_all() + print("Creating transactions and mining them with a block size that can't keep up") + # Create transactions and mine 30 small blocks with node 2, but create txs faster than we can mine + self.transact_and_mine(20, self.nodes[2]) + check_estimates(self.nodes[1], self.fees_per_kb, 3) - all_estimates = [ self.nodes[0].estimatefee(i) for i in range(1,20) ] - print("Fee estimates, more generous miner: "+str([ str(e) for e in all_estimates])) - for e in filter(lambda x: x >= 0, all_estimates): - if float(e)+delta < min(fees_per_kb) or float(e)-delta > max(fees_per_kb): - raise AssertionError("Estimated fee (%f) out of range (%f,%f)"%(float(e), min_fee_kb, max_fee_kb)) + print("Creating transactions and mining them at a block size that is just big enough") + # Generate transactions while mining 40 more blocks, this time with node1 + # which mines blocks with capacity just above the rate that transactions are being created + self.transact_and_mine(40, self.nodes[1]) + check_estimates(self.nodes[1], self.fees_per_kb, 2) # Finish by mining a normal-sized block: - while len(self.nodes[0].getrawmempool()) > 0: - self.nodes[0].generate(1) - self.sync_all() - - final_estimates = [ self.nodes[0].estimatefee(i) for i in range(1,20) ] - print("Final fee estimates: "+str([ str(e) for e in final_estimates])) + while len(self.nodes[1].getrawmempool()) > 0: + self.nodes[1].generate(1) + sync_blocks(self.nodes[0:3],.1) + print("Final estimates after emptying mempools") + check_estimates(self.nodes[1], self.fees_per_kb, 2) if __name__ == '__main__': EstimateFeeTest().main() diff --git a/qa/rpc-tests/python-bitcoinrpc/bitcoinrpc/__init__.py b/qa/rpc-tests/test_framework/__init__.py index e69de29bb2..e69de29bb2 100644 --- a/qa/rpc-tests/python-bitcoinrpc/bitcoinrpc/__init__.py +++ b/qa/rpc-tests/test_framework/__init__.py diff --git a/qa/rpc-tests/python-bitcoinrpc/bitcoinrpc/authproxy.py b/qa/rpc-tests/test_framework/authproxy.py index bc7d655fdf..bc7d655fdf 100644 --- a/qa/rpc-tests/python-bitcoinrpc/bitcoinrpc/authproxy.py +++ b/qa/rpc-tests/test_framework/authproxy.py diff --git a/qa/rpc-tests/bignum.py b/qa/rpc-tests/test_framework/bignum.py index b0c58ccd47..b0c58ccd47 100644 --- a/qa/rpc-tests/bignum.py +++ b/qa/rpc-tests/test_framework/bignum.py diff --git a/qa/rpc-tests/blockstore.py b/qa/rpc-tests/test_framework/blockstore.py index c57b6df81b..c57b6df81b 100644 --- a/qa/rpc-tests/blockstore.py +++ b/qa/rpc-tests/test_framework/blockstore.py diff --git a/qa/rpc-tests/blocktools.py b/qa/rpc-tests/test_framework/blocktools.py index f397fe7cd6..f397fe7cd6 100644 --- a/qa/rpc-tests/blocktools.py +++ b/qa/rpc-tests/test_framework/blocktools.py diff --git a/qa/rpc-tests/comptool.py b/qa/rpc-tests/test_framework/comptool.py index 23a979250c..23a979250c 100755 --- a/qa/rpc-tests/comptool.py +++ b/qa/rpc-tests/test_framework/comptool.py diff --git a/qa/rpc-tests/mininode.py b/qa/rpc-tests/test_framework/mininode.py index b7d78e74fa..b7d78e74fa 100755 --- a/qa/rpc-tests/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py diff --git a/qa/rpc-tests/netutil.py b/qa/rpc-tests/test_framework/netutil.py index b30a88a4f7..b30a88a4f7 100644 --- a/qa/rpc-tests/netutil.py +++ b/qa/rpc-tests/test_framework/netutil.py diff --git a/qa/rpc-tests/script.py b/qa/rpc-tests/test_framework/script.py index 03695b8635..e37ab5d45a 100644 --- a/qa/rpc-tests/script.py +++ b/qa/rpc-tests/test_framework/script.py @@ -14,7 +14,7 @@ Functionality to build scripts, as well as SignatureHash(). from __future__ import absolute_import, division, print_function, unicode_literals -from mininode import CTransaction, CTxOut, hash256 +from test_framework.mininode import CTransaction, CTxOut, hash256 import sys bchr = chr @@ -27,7 +27,7 @@ if sys.version > '3': import copy import struct -import bignum +import test_framework.bignum MAX_SCRIPT_SIZE = 10000 MAX_SCRIPT_ELEMENT_SIZE = 520 diff --git a/qa/rpc-tests/socks5.py b/qa/rpc-tests/test_framework/socks5.py index 1dbfb98d5d..1dbfb98d5d 100644 --- a/qa/rpc-tests/socks5.py +++ b/qa/rpc-tests/test_framework/socks5.py diff --git a/qa/rpc-tests/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py index 15a357a340..5671431f6e 100755 --- a/qa/rpc-tests/test_framework.py +++ b/qa/rpc-tests/test_framework/test_framework.py @@ -8,13 +8,12 @@ # Add python-bitcoinrpc to module search path: import os import sys -sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "python-bitcoinrpc")) import shutil import tempfile import traceback -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException +from authproxy import AuthServiceProxy, JSONRPCException from util import * diff --git a/qa/rpc-tests/util.py b/qa/rpc-tests/test_framework/util.py index 3b4a10e46b..c236ec2602 100644 --- a/qa/rpc-tests/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -8,7 +8,6 @@ # Add python-bitcoinrpc to module search path: import os import sys -sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "python-bitcoinrpc")) from decimal import Decimal, ROUND_DOWN import json @@ -18,7 +17,7 @@ import subprocess import time import re -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException +from authproxy import AuthServiceProxy, JSONRPCException from util import * def p2p_port(n): @@ -33,7 +32,7 @@ def check_json_precision(): if satoshis != 2000000000000003: raise RuntimeError("JSON encode/decode loses precision") -def sync_blocks(rpc_connections): +def sync_blocks(rpc_connections, wait=1): """ Wait until everybody has the same block count """ @@ -41,9 +40,9 @@ def sync_blocks(rpc_connections): counts = [ x.getblockcount() for x in rpc_connections ] if counts == [ counts[0] ]*len(counts): break - time.sleep(1) + time.sleep(wait) -def sync_mempools(rpc_connections): +def sync_mempools(rpc_connections, wait=1): """ Wait until everybody has the same transactions in their memory pools @@ -56,7 +55,7 @@ def sync_mempools(rpc_connections): num_match = num_match+1 if num_match == len(rpc_connections): break - time.sleep(1) + time.sleep(wait) bitcoind_processes = {} diff --git a/qa/rpc-tests/txn_doublespend.py b/qa/rpc-tests/txn_doublespend.py index fe9168944b..99dcdae552 100755 --- a/qa/rpc-tests/txn_doublespend.py +++ b/qa/rpc-tests/txn_doublespend.py @@ -7,10 +7,9 @@ # Test proper accounting with malleable transactions # -from test_framework import BitcoinTestFramework -from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * from decimal import Decimal -from util import * import os import shutil diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py index b8965b3662..46dc7765b6 100755 --- a/qa/rpc-tests/wallet.py +++ b/qa/rpc-tests/wallet.py @@ -19,9 +19,8 @@ # k) test ResendWalletTransactions - create transactions, startup fourth node, make sure it syncs # -from test_framework import BitcoinTestFramework -from util import * - +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * class WalletTest (BitcoinTestFramework): diff --git a/qa/rpc-tests/walletbackup.py b/qa/rpc-tests/walletbackup.py index b9fc862234..da100d7fc0 100755 --- a/qa/rpc-tests/walletbackup.py +++ b/qa/rpc-tests/walletbackup.py @@ -33,8 +33,8 @@ Shutdown again, restore using importwallet, and confirm again balances are correct. """ -from test_framework import BitcoinTestFramework -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * from random import randint import logging logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO) diff --git a/qa/rpc-tests/zapwallettxes.py b/qa/rpc-tests/zapwallettxes.py index 045614e94c..0ec8ec5364 100755 --- a/qa/rpc-tests/zapwallettxes.py +++ b/qa/rpc-tests/zapwallettxes.py @@ -3,8 +3,8 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -from test_framework import BitcoinTestFramework -from util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * class ZapWalletTXesTest (BitcoinTestFramework): diff --git a/share/qt/Info.plist.in b/share/qt/Info.plist.in index dd6edde8d2..a389332a52 100644 --- a/share/qt/Info.plist.in +++ b/share/qt/Info.plist.in @@ -3,7 +3,7 @@ <plist version="0.9"> <dict> <key>LSMinimumSystemVersion</key> - <string>10.6.0</string> + <string>10.7.0</string> <key>LSArchitecturePriority</key> <array> @@ -30,6 +30,12 @@ <key>CFBundleExecutable</key> <string>Bitcoin-Qt</string> + + <key>CFBundleName</key> + <string>Bitcoin-Qt</string> + + <key>LSHasLocalizedDisplayName</key> + <true/> <key>CFBundleIdentifier</key> <string>org.bitcoinfoundation.Bitcoin-Qt</string> diff --git a/src/Makefile.am b/src/Makefile.am index 72d79619b2..59618c4940 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,7 +14,7 @@ $(LIBLEVELDB): $(LIBMEMENV) $(LIBLEVELDB) $(LIBMEMENV): @echo "Building LevelDB ..." && $(MAKE) -C $(@D) $(@F) CXX="$(CXX)" \ CC="$(CC)" PLATFORM=$(TARGET_OS) AR="$(AR)" $(LEVELDB_TARGET_FLAGS) \ - OPT="$(CXXFLAGS) $(CPPFLAGS)" + OPT="$(CXXFLAGS) $(CPPFLAGS) -D__STDC_LIMIT_MACROS" endif BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config @@ -77,8 +77,8 @@ BITCOIN_CORE_H = \ base58.h \ bloom.h \ chain.h \ - chainparamsbase.h \ chainparams.h \ + chainparamsbase.h \ chainparamsseeds.h \ checkpoints.h \ checkqueue.h \ @@ -86,11 +86,14 @@ BITCOIN_CORE_H = \ coincontrol.h \ coins.h \ compat.h \ + compat/byteswap.h \ + compat/endian.h \ + compat/sanity.h \ compressor.h \ consensus/consensus.h \ consensus/params.h \ + consensus/validation.h \ core_io.h \ - wallet/db.h \ eccryptoverify.h \ ecwrapper.h \ hash.h \ @@ -100,12 +103,14 @@ BITCOIN_CORE_H = \ leveldbwrapper.h \ limitedmap.h \ main.h \ + memusage.h \ merkleblock.h \ miner.h \ mruset.h \ - netbase.h \ net.h \ + netbase.h \ noui.h \ + policy/fees.h \ pow.h \ primitives/block.h \ primitives/transaction.h \ @@ -115,9 +120,10 @@ BITCOIN_CORE_H = \ rpcclient.h \ rpcprotocol.h \ rpcserver.h \ + scheduler.h \ script/interpreter.h \ - script/script_error.h \ script/script.h \ + script/script_error.h \ script/sigcache.h \ script/sign.h \ script/standard.h \ @@ -143,23 +149,10 @@ BITCOIN_CORE_H = \ validationinterface.h \ version.h \ wallet/crypter.h \ - wallet/walletdb.h \ + wallet/db.h \ wallet/wallet.h \ wallet/wallet_ismine.h \ - compat/byteswap.h \ - compat/endian.h \ - compat/sanity.h - -JSON_H = \ - json/json_spirit.h \ - json/json_spirit_error_position.h \ - json/json_spirit_reader.h \ - json/json_spirit_reader_template.h \ - json/json_spirit_stream_reader.h \ - json/json_spirit_utils.h \ - json/json_spirit_value.h \ - json/json_spirit_writer.h \ - json/json_spirit_writer_template.h + wallet/walletdb.h obj/build.h: FORCE @$(MKDIR_P) $(builddir)/obj @@ -182,6 +175,7 @@ libbitcoin_server_a_SOURCES = \ miner.cpp \ net.cpp \ noui.cpp \ + policy/fees.cpp \ pow.cpp \ rest.cpp \ rpcblockchain.cpp \ @@ -195,7 +189,6 @@ libbitcoin_server_a_SOURCES = \ txdb.cpp \ txmempool.cpp \ validationinterface.cpp \ - $(JSON_H) \ $(BITCOIN_CORE_H) # wallet: shared between bitcoind and bitcoin-qt, but only linked @@ -214,39 +207,37 @@ libbitcoin_wallet_a_SOURCES = \ # crypto primitives library crypto_libbitcoin_crypto_a_CPPFLAGS = $(BITCOIN_CONFIG_INCLUDES) crypto_libbitcoin_crypto_a_SOURCES = \ - crypto/sha1.cpp \ - crypto/sha256.cpp \ - crypto/sha512.cpp \ - crypto/hmac_sha256.cpp \ - crypto/hmac_sha512.cpp \ - crypto/ripemd160.cpp \ crypto/common.h \ - crypto/sha256.h \ - crypto/sha512.h \ + crypto/hmac_sha256.cpp \ crypto/hmac_sha256.h \ + crypto/hmac_sha512.cpp \ crypto/hmac_sha512.h \ + crypto/ripemd160.cpp \ + crypto/ripemd160.h \ + crypto/sha1.cpp \ crypto/sha1.h \ - crypto/ripemd160.h + crypto/sha256.cpp \ + crypto/sha256.h \ + crypto/sha512.cpp \ + crypto/sha512.h # univalue JSON library univalue_libbitcoin_univalue_a_SOURCES = \ univalue/univalue.cpp \ - univalue/univalue_read.cpp \ - univalue/univalue_write.cpp \ + univalue/univalue.h \ univalue/univalue_escapes.h \ - univalue/univalue.h + univalue/univalue_read.cpp \ + univalue/univalue_write.cpp # common: shared between bitcoind, and bitcoin-qt and non-server tools libbitcoin_common_a_CPPFLAGS = $(BITCOIN_INCLUDES) libbitcoin_common_a_SOURCES = \ - arith_uint256.cpp \ amount.cpp \ + arith_uint256.cpp \ base58.cpp \ chainparams.cpp \ coins.cpp \ compressor.cpp \ - primitives/block.cpp \ - primitives/transaction.cpp \ core_read.cpp \ core_write.cpp \ eccryptoverify.cpp \ @@ -255,13 +246,16 @@ libbitcoin_common_a_SOURCES = \ key.cpp \ keystore.cpp \ netbase.cpp \ + primitives/block.cpp \ + primitives/transaction.cpp \ protocol.cpp \ pubkey.cpp \ + scheduler.cpp \ script/interpreter.cpp \ script/script.cpp \ + script/script_error.cpp \ script/sign.cpp \ script/standard.cpp \ - script/script_error.cpp \ $(BITCOIN_CORE_H) # util: shared between all executables. @@ -336,6 +330,7 @@ endif bitcoin_cli_LDADD = \ $(LIBBITCOIN_CLI) \ + $(LIBBITCOIN_UNIVALUE) \ $(LIBBITCOIN_UTIL) \ $(LIBSECP256K1) @@ -347,6 +342,10 @@ bitcoin_tx_SOURCES = bitcoin-tx.cpp bitcoin_tx_CPPFLAGS = $(BITCOIN_INCLUDES) bitcoin_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +if TARGET_WINDOWS +bitcoin_tx_SOURCES += bitcoin-tx-res.rc +endif + bitcoin_tx_LDADD = \ $(LIBBITCOIN_UNIVALUE) \ $(LIBBITCOIN_COMMON) \ diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 31fe3a9f69..6b7c42285d 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -256,6 +256,7 @@ RES_ICONS = \ qt/res/icons/tx_input.png \ qt/res/icons/tx_output.png \ qt/res/icons/tx_mined.png \ + qt/res/icons/warning.png \ qt/res/icons/verify.png BITCOIN_QT_CPP = \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 9aec13ccef..0997148117 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -57,9 +57,11 @@ BITCOIN_TESTS =\ test/multisig_tests.cpp \ test/netbase_tests.cpp \ test/pmt_tests.cpp \ + test/policyestimator_tests.cpp \ test/pow_tests.cpp \ test/rpc_tests.cpp \ test/sanity_tests.cpp \ + test/scheduler_tests.cpp \ test/script_P2SH_tests.cpp \ test/script_tests.cpp \ test/scriptnum_tests.cpp \ diff --git a/src/addrman.h b/src/addrman.h index b72dda49d1..373b0f39f3 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -110,10 +110,10 @@ public: * * To that end: * * Addresses are organized into buckets. - * * Address that have not yet been tried go into 1024 "new" buckets. - * * Based on the address range (/16 for IPv4) of source of the information, 64 buckets are selected at random + * * Addresses that have not yet been tried go into 1024 "new" buckets. + * * Based on the address range (/16 for IPv4) of the source of information, 64 buckets are selected at random. * * The actual bucket is chosen from one of these, based on the range in which the address itself is located. - * * One single address can occur in up to 8 different buckets, to increase selection chances for addresses that + * * One single address can occur in up to 8 different buckets to increase selection chances for addresses that * are seen frequently. The chance for increasing this multiplicity decreases exponentially. * * When adding a new address to a full bucket, a randomly chosen entry (with a bias favoring less recently seen * ones) is removed from it first. @@ -231,7 +231,6 @@ protected: void Attempt_(const CService &addr, int64_t nTime); //! Select an address to connect to. - //! nUnkBias determines how much to favor new addresses over tried ones (min=0, max=100) CAddrInfo Select_(); #ifdef DEBUG_ADDRMAN @@ -532,7 +531,6 @@ public: /** * Choose an address to connect to. - * nUnkBias determines how much "new" entries are favored over "tried" ones (0-100). */ CAddrInfo Select() { diff --git a/src/alert.cpp b/src/alert.cpp index aa7ac748da..ad81e74226 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -50,7 +50,7 @@ std::string CUnsignedAlert::ToString() const BOOST_FOREACH(int n, setCancel) strSetCancel += strprintf("%d ", n); std::string strSetSubVer; - BOOST_FOREACH(std::string str, setSubVer) + BOOST_FOREACH(const std::string& str, setSubVer) strSetSubVer += "\"" + str + "\" "; return strprintf( "CAlert(\n" @@ -110,7 +110,7 @@ bool CAlert::Cancels(const CAlert& alert) const return (alert.nID <= nCancel || setCancel.count(alert.nID)); } -bool CAlert::AppliesTo(int nVersion, std::string strSubVerIn) const +bool CAlert::AppliesTo(int nVersion, const std::string& strSubVerIn) const { // TODO: rework for client-version-embedded-in-strSubVer ? return (IsInEffect() && diff --git a/src/alert.h b/src/alert.h index 746967c4af..4f9fff9181 100644 --- a/src/alert.h +++ b/src/alert.h @@ -97,7 +97,7 @@ public: uint256 GetHash() const; bool IsInEffect() const; bool Cancels(const CAlert& alert) const; - bool AppliesTo(int nVersion, std::string strSubVerIn) const; + bool AppliesTo(int nVersion, const std::string& strSubVerIn) const; bool AppliesToMe() const; bool RelayTo(CNode* pnode) const; bool CheckSignature(const std::vector<unsigned char>& alertKey) const; diff --git a/src/amount.h b/src/amount.h index 9212244a88..7dc62edac4 100644 --- a/src/amount.h +++ b/src/amount.h @@ -16,7 +16,15 @@ typedef int64_t CAmount; static const CAmount COIN = 100000000; static const CAmount CENT = 1000000; -/** No amount larger than this (in satoshi) is valid */ +/** No amount larger than this (in satoshi) is valid. + * + * Note that this constant is *not* the total money supply, which in Bitcoin + * currently happens to be less than 21,000,000 BTC for various reasons, but + * rather a sanity check. As this sanity check is used by consensus-critical + * validation code, the exact value of the MAX_MONEY constant is consensus + * critical; in unusual circumstances like a(nother) overflow bug that allowed + * for the creation of coins out of thin air modification could lead to a fork. + * */ static const CAmount MAX_MONEY = 21000000 * COIN; inline bool MoneyRange(const CAmount& nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } diff --git a/src/bitcoin-cli-res.rc b/src/bitcoin-cli-res.rc index 4ea1f38e47..1e4aa609bb 100644 --- a/src/bitcoin-cli-res.rc +++ b/src/bitcoin-cli-res.rc @@ -17,13 +17,13 @@ BEGIN BLOCK "040904E4" // U.S. English - multilingual (hex) BEGIN VALUE "CompanyName", "Bitcoin" - VALUE "FileDescription", "Bitcoin-cli (OSS RPC client for Bitcoin)" + VALUE "FileDescription", "bitcoin-cli (JSON-RPC client for Bitcoin Core)" VALUE "FileVersion", VER_FILEVERSION_STR VALUE "InternalName", "bitcoin-cli" VALUE "LegalCopyright", COPYRIGHT_STR VALUE "LegalTrademarks1", "Distributed under the MIT software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php." VALUE "OriginalFilename", "bitcoin-cli.exe" - VALUE "ProductName", "Bitcoin-cli" + VALUE "ProductName", "bitcoin-cli" VALUE "ProductVersion", VER_PRODUCTVERSION_STR END END diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 1269d7a119..d451720141 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -12,8 +12,9 @@ #include <boost/filesystem/operations.hpp> +#include "univalue/univalue.h" + using namespace std; -using namespace json_spirit; std::string HelpMessageCli() { @@ -94,7 +95,7 @@ static bool AppInitRPC(int argc, char* argv[]) return true; } -Object CallRPC(const string& strMethod, const Array& params) +UniValue CallRPC(const string& strMethod, const UniValue& params) { if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") throw runtime_error(strprintf( @@ -142,10 +143,10 @@ Object CallRPC(const string& strMethod, const Array& params) throw runtime_error("no response from server"); // Parse reply - Value valReply; - if (!read_string(strReply, valReply)) + UniValue valReply(UniValue::VSTR); + if (!valReply.read(strReply)) throw runtime_error("couldn't parse reply from server"); - const Object& reply = valReply.get_obj(); + const UniValue& reply = valReply.get_obj(); if (reply.empty()) throw runtime_error("expected reply to have result, error and id properties"); @@ -170,35 +171,34 @@ int CommandLineRPC(int argc, char *argv[]) // Parameters default to strings std::vector<std::string> strParams(&argv[2], &argv[argc]); - Array params = RPCConvertValues(strMethod, strParams); + UniValue params = RPCConvertValues(strMethod, strParams); // Execute and handle connection failures with -rpcwait const bool fWait = GetBoolArg("-rpcwait", false); do { try { - const Object reply = CallRPC(strMethod, params); + const UniValue reply = CallRPC(strMethod, params); // Parse reply - const Value& result = find_value(reply, "result"); - const Value& error = find_value(reply, "error"); + const UniValue& result = find_value(reply, "result"); + const UniValue& error = find_value(reply, "error"); - if (error.type() != null_type) { + if (!error.isNull()) { // Error - const int code = find_value(error.get_obj(), "code").get_int(); + int code = error["code"].get_int(); if (fWait && code == RPC_IN_WARMUP) throw CConnectionFailed("server in warmup"); - strPrint = "error: " + write_string(error, false); + strPrint = "error: " + error.write(); nRet = abs(code); } else { // Result - if (result.type() == null_type) + if (result.isNull()) strPrint = ""; - else if (result.type() == str_type) + else if (result.isStr()) strPrint = result.get_str(); else - strPrint = write_string(result, true); + strPrint = result.write(2); } - // Connection succeeded, no need to retry. break; } diff --git a/src/bitcoin-tx-res.rc b/src/bitcoin-tx-res.rc new file mode 100644 index 0000000000..3e49b820bc --- /dev/null +++ b/src/bitcoin-tx-res.rc @@ -0,0 +1,35 @@ +#include <windows.h> // needed for VERSIONINFO +#include "clientversion.h" // holds the needed client version information + +#define VER_PRODUCTVERSION CLIENT_VERSION_MAJOR,CLIENT_VERSION_MINOR,CLIENT_VERSION_REVISION,CLIENT_VERSION_BUILD +#define VER_PRODUCTVERSION_STR STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_REVISION) "." STRINGIZE(CLIENT_VERSION_BUILD) +#define VER_FILEVERSION VER_PRODUCTVERSION +#define VER_FILEVERSION_STR VER_PRODUCTVERSION_STR + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" // U.S. English - multilingual (hex) + BEGIN + VALUE "CompanyName", "Bitcoin" + VALUE "FileDescription", "bitcoin-tx (CLI Bitcoin transaction editor utility)" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "bitcoin-tx" + VALUE "LegalCopyright", COPYRIGHT_STR + VALUE "LegalTrademarks1", "Distributed under the MIT software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php." + VALUE "OriginalFilename", "bitcoin-tx.exe" + VALUE "ProductName", "bitcoin-tx" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1252 // language neutral - multilingual (decimal) + END +END diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index c82d4f93a8..45990f6bd8 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -346,7 +346,7 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) UniValue keysObj = registers["privatekeys"]; fGivenKeys = true; - for (unsigned int kidx = 0; kidx < keysObj.count(); kidx++) { + for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) { if (!keysObj[kidx].isStr()) throw runtime_error("privatekey not a string"); CBitcoinSecret vchSecret; @@ -363,7 +363,7 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) throw runtime_error("prevtxs register variable must be set."); UniValue prevtxsObj = registers["prevtxs"]; { - for (unsigned int previdx = 0; previdx < prevtxsObj.count(); previdx++) { + for (unsigned int previdx = 0; previdx < prevtxsObj.size(); previdx++) { UniValue prevOut = prevtxsObj[previdx]; if (!prevOut.isObject()) throw runtime_error("expected prevtxs internal object"); diff --git a/src/bitcoind-res.rc b/src/bitcoind-res.rc index d183179b17..3a64acd5d1 100644 --- a/src/bitcoind-res.rc +++ b/src/bitcoind-res.rc @@ -17,13 +17,13 @@ BEGIN BLOCK "040904E4" // U.S. English - multilingual (hex) BEGIN VALUE "CompanyName", "Bitcoin" - VALUE "FileDescription", "Bitcoind (OSS daemon/client for Bitcoin)" + VALUE "FileDescription", "bitcoind (Bitcoin node with a JSON-RPC server)" VALUE "FileVersion", VER_FILEVERSION_STR VALUE "InternalName", "bitcoind" VALUE "LegalCopyright", COPYRIGHT_STR VALUE "LegalTrademarks1", "Distributed under the MIT software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php." VALUE "OriginalFilename", "bitcoind.exe" - VALUE "ProductName", "Bitcoind" + VALUE "ProductName", "bitcoind" VALUE "ProductVersion", VER_PRODUCTVERSION_STR END END diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index eeca8655c9..cce687ac98 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -8,6 +8,7 @@ #include "init.h" #include "main.h" #include "noui.h" +#include "scheduler.h" #include "util.h" #include <boost/algorithm/string/predicate.hpp> @@ -55,6 +56,7 @@ void WaitForShutdown(boost::thread_group* threadGroup) bool AppInit(int argc, char* argv[]) { boost::thread_group threadGroup; + CScheduler scheduler; bool fRet = false; @@ -142,7 +144,7 @@ bool AppInit(int argc, char* argv[]) #endif SoftSetBoolArg("-server", true); - fRet = AppInit2(threadGroup); + fRet = AppInit2(threadGroup, scheduler); } catch (const std::exception& e) { PrintExceptionContinue(&e, "AppInit()"); diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 556d0aed4d..7785417518 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -66,6 +66,7 @@ public: */ const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"; CMutableTransaction txNew; + txNew.nVersion = 1; txNew.vin.resize(1); txNew.vout.resize(1); txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(4) << vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); @@ -86,7 +87,7 @@ public: vSeeds.push_back(CDNSSeedData("bitcoin.sipa.be", "seed.bitcoin.sipa.be")); // Pieter Wuille vSeeds.push_back(CDNSSeedData("bluematt.me", "dnsseed.bluematt.me")); // Matt Corallo vSeeds.push_back(CDNSSeedData("dashjr.org", "dnsseed.bitcoin.dashjr.org")); // Luke Dashjr - vSeeds.push_back(CDNSSeedData("bitcoinstats.com", "seed.bitcoinstats.com")); // Addy Yeow + vSeeds.push_back(CDNSSeedData("bitcoinstats.com", "seed.bitcoinstats.com")); // Christian Decker vSeeds.push_back(CDNSSeedData("xf2.org", "bitseed.xf2.org")); // Jeff Garzik vSeeds.push_back(CDNSSeedData("bitcoin.jonasschnelli.ch", "seed.bitcoin.jonasschnelli.ch")); // Jonas Schnelli diff --git a/src/chainparams.h b/src/chainparams.h index bb95b3aa4a..8044b553e1 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -49,10 +49,6 @@ public: const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; } const std::vector<unsigned char>& AlertKey() const { return vAlertPubKey; } int GetDefaultPort() const { return nDefaultPort; } - int SubsidyHalvingInterval() const { return consensus.nSubsidyHalvingInterval; } - int EnforceBlockUpgradeMajority() const { return consensus.nMajorityEnforceBlockUpgrade; } - int RejectBlockOutdatedMajority() const { return consensus.nMajorityRejectBlockOutdated; } - int ToCheckBlockUpgradeMajority() const { return consensus.nMajorityWindow; } /** Used if GenerateBitcoins is called with a negative number of threads */ int DefaultMinerThreads() const { return nMinerThreads; } diff --git a/src/clientversion.h b/src/clientversion.h index 3c8c969f9d..5a06b310a3 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -15,7 +15,7 @@ //! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 0 -#define CLIENT_VERSION_MINOR 10 +#define CLIENT_VERSION_MINOR 11 #define CLIENT_VERSION_REVISION 99 #define CLIENT_VERSION_BUILD 0 diff --git a/src/coins.cpp b/src/coins.cpp index d79e29951b..a41d5a310d 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -4,6 +4,7 @@ #include "coins.h" +#include "memusage.h" #include "random.h" #include <assert.h> @@ -57,13 +58,17 @@ bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { return base->GetStat CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {} -CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false) { } +CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false), cachedCoinsUsage(0) { } CCoinsViewCache::~CCoinsViewCache() { assert(!hasModifier); } +size_t CCoinsViewCache::DynamicMemoryUsage() const { + return memusage::DynamicUsage(cacheCoins) + cachedCoinsUsage; +} + CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const { CCoinsMap::iterator it = cacheCoins.find(txid); if (it != cacheCoins.end()) @@ -78,6 +83,7 @@ CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const // version as fresh. ret->second.flags = CCoinsCacheEntry::FRESH; } + cachedCoinsUsage += memusage::DynamicUsage(ret->second.coins); return ret; } @@ -93,6 +99,7 @@ bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const { CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256 &txid) { assert(!hasModifier); std::pair<CCoinsMap::iterator, bool> ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry())); + size_t cachedCoinUsage = 0; if (ret.second) { if (!base->GetCoins(txid, ret.first->second.coins)) { // The parent view does not have this entry; mark it as fresh. @@ -102,10 +109,12 @@ CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256 &txid) { // The parent view only has a pruned entry for this; mark it as fresh. ret.first->second.flags = CCoinsCacheEntry::FRESH; } + } else { + cachedCoinUsage = memusage::DynamicUsage(ret.first->second.coins); } // Assume that whenever ModifyCoins is called, the entry will be modified. ret.first->second.flags |= CCoinsCacheEntry::DIRTY; - return CCoinsModifier(*this, ret.first); + return CCoinsModifier(*this, ret.first, cachedCoinUsage); } const CCoins* CCoinsViewCache::AccessCoins(const uint256 &txid) const { @@ -150,6 +159,7 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn assert(it->second.flags & CCoinsCacheEntry::FRESH); CCoinsCacheEntry& entry = cacheCoins[it->first]; entry.coins.swap(it->second.coins); + cachedCoinsUsage += memusage::DynamicUsage(entry.coins); entry.flags = CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH; } } else { @@ -157,10 +167,13 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn // The grandparent does not have an entry, and the child is // modified and being pruned. This means we can just delete // it from the parent. + cachedCoinsUsage -= memusage::DynamicUsage(itUs->second.coins); cacheCoins.erase(itUs); } else { // A normal modification. + cachedCoinsUsage -= memusage::DynamicUsage(itUs->second.coins); itUs->second.coins.swap(it->second.coins); + cachedCoinsUsage += memusage::DynamicUsage(itUs->second.coins); itUs->second.flags |= CCoinsCacheEntry::DIRTY; } } @@ -175,6 +188,7 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn bool CCoinsViewCache::Flush() { bool fOk = base->BatchWrite(cacheCoins, hashBlock); cacheCoins.clear(); + cachedCoinsUsage = 0; return fOk; } @@ -232,7 +246,7 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const return tx.ComputePriority(dResult); } -CCoinsModifier::CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_) : cache(cache_), it(it_) { +CCoinsModifier::CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_, size_t usage) : cache(cache_), it(it_), cachedCoinUsage(usage) { assert(!cache.hasModifier); cache.hasModifier = true; } @@ -242,7 +256,11 @@ CCoinsModifier::~CCoinsModifier() assert(cache.hasModifier); cache.hasModifier = false; it->second.coins.Cleanup(); + cache.cachedCoinsUsage -= cachedCoinUsage; // Subtract the old usage if ((it->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) { cache.cacheCoins.erase(it); + } else { + // If the coin still exists after the modification, add the new usage + cache.cachedCoinsUsage += memusage::DynamicUsage(it->second.coins); } } diff --git a/src/coins.h b/src/coins.h index fe2eaa08e5..a4671645df 100644 --- a/src/coins.h +++ b/src/coins.h @@ -7,6 +7,7 @@ #define BITCOIN_COINS_H #include "compressor.h" +#include "memusage.h" #include "serialize.h" #include "uint256.h" @@ -252,6 +253,15 @@ public: return false; return true; } + + size_t DynamicMemoryUsage() const { + size_t ret = memusage::DynamicUsage(vout); + BOOST_FOREACH(const CTxOut &out, vout) { + const std::vector<unsigned char> *script = &out.scriptPubKey; + ret += memusage::DynamicUsage(*script); + } + return ret; + } }; class CCoinsKeyHasher @@ -356,7 +366,8 @@ class CCoinsModifier private: CCoinsViewCache& cache; CCoinsMap::iterator it; - CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_); + size_t cachedCoinUsage; // Cached memory usage of the CCoins object before modification + CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_, size_t usage); public: CCoins* operator->() { return &it->second.coins; } @@ -372,6 +383,7 @@ protected: /* Whether this cache has an active modifier. */ bool hasModifier; + /** * Make mutable so that we can "fill the cache" even from Get-methods * declared as "const". @@ -379,6 +391,9 @@ protected: mutable uint256 hashBlock; mutable CCoinsMap cacheCoins; + /* Cached dynamic memory usage for the inner CCoins objects. */ + mutable size_t cachedCoinsUsage; + public: CCoinsViewCache(CCoinsView *baseIn); ~CCoinsViewCache(); @@ -414,6 +429,9 @@ public: //! Calculate the size of the cache (in number of transactions) unsigned int GetCacheSize() const; + //! Calculate the size of the cache (in bytes) + size_t DynamicMemoryUsage() const; + /** * Amount of bitcoins coming in to a transaction * Note that lightweight clients may not know anything besides the hash of previous transactions, diff --git a/src/compat/endian.h b/src/compat/endian.h index 4d041d6554..9fec2a07fa 100644 --- a/src/compat/endian.h +++ b/src/compat/endian.h @@ -15,6 +15,8 @@ #if defined(HAVE_ENDIAN_H) #include <endian.h> +#elif defined(HAVE_SYS_ENDIAN_H) +#include <sys/endian.h> #endif #if defined(WORDS_BIGENDIAN) diff --git a/src/consensus/validation.h b/src/consensus/validation.h new file mode 100644 index 0000000000..a97d983a31 --- /dev/null +++ b/src/consensus/validation.h @@ -0,0 +1,80 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CONSENSUS_VALIDATION_H +#define BITCOIN_CONSENSUS_VALIDATION_H + +#include <string> + +/** "reject" message codes */ +static const unsigned char REJECT_MALFORMED = 0x01; +static const unsigned char REJECT_INVALID = 0x10; +static const unsigned char REJECT_OBSOLETE = 0x11; +static const unsigned char REJECT_DUPLICATE = 0x12; +static const unsigned char REJECT_NONSTANDARD = 0x40; +static const unsigned char REJECT_DUST = 0x41; +static const unsigned char REJECT_INSUFFICIENTFEE = 0x42; +static const unsigned char REJECT_CHECKPOINT = 0x43; + +/** Capture information about block/transaction validation */ +class CValidationState { +private: + enum mode_state { + MODE_VALID, //! everything ok + MODE_INVALID, //! network rule violation (DoS value may be set) + MODE_ERROR, //! run-time error + } mode; + int nDoS; + std::string strRejectReason; + unsigned char chRejectCode; + bool corruptionPossible; +public: + CValidationState() : mode(MODE_VALID), nDoS(0), chRejectCode(0), corruptionPossible(false) {} + bool DoS(int level, bool ret = false, + unsigned char chRejectCodeIn=0, std::string strRejectReasonIn="", + bool corruptionIn=false) { + chRejectCode = chRejectCodeIn; + strRejectReason = strRejectReasonIn; + corruptionPossible = corruptionIn; + if (mode == MODE_ERROR) + return ret; + nDoS += level; + mode = MODE_INVALID; + return ret; + } + bool Invalid(bool ret = false, + unsigned char _chRejectCode=0, std::string _strRejectReason="") { + return DoS(0, ret, _chRejectCode, _strRejectReason); + } + bool Error(const std::string& strRejectReasonIn) { + if (mode == MODE_VALID) + strRejectReason = strRejectReasonIn; + mode = MODE_ERROR; + return false; + } + bool IsValid() const { + return mode == MODE_VALID; + } + bool IsInvalid() const { + return mode == MODE_INVALID; + } + bool IsError() const { + return mode == MODE_ERROR; + } + bool IsInvalid(int &nDoSOut) const { + if (IsInvalid()) { + nDoSOut = nDoS; + return true; + } + return false; + } + bool CorruptionPossible() const { + return corruptionPossible; + } + unsigned char GetRejectCode() const { return chRejectCode; } + std::string GetRejectReason() const { return strRejectReason; } +}; + +#endif // BITCOIN_CONSENSUS_VALIDATION_H diff --git a/src/core_io.h b/src/core_io.h index 0989cf7437..115e3199dc 100644 --- a/src/core_io.h +++ b/src/core_io.h @@ -15,7 +15,7 @@ class uint256; class UniValue; // core_read.cpp -extern CScript ParseScript(std::string s); +extern CScript ParseScript(const std::string& s); extern bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx); extern bool DecodeHexBlk(CBlock&, const std::string& strHexBlk); extern uint256 ParseHashUV(const UniValue& v, const std::string& strName); diff --git a/src/core_read.cpp b/src/core_read.cpp index e064955ff0..f762f2c3b7 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -22,7 +22,7 @@ using namespace std; -CScript ParseScript(std::string s) +CScript ParseScript(const std::string& s) { CScript result; diff --git a/src/init.cpp b/src/init.cpp index 1905bbf8b8..43714d22b8 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -13,12 +13,14 @@ #include "amount.h" #include "checkpoints.h" #include "compat/sanity.h" +#include "consensus/validation.h" #include "key.h" #include "main.h" #include "miner.h" #include "net.h" #include "rpcserver.h" #include "script/standard.h" +#include "scheduler.h" #include "txdb.h" #include "ui_interface.h" #include "util.h" @@ -38,7 +40,9 @@ #include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/replace.hpp> +#include <boost/bind.hpp> #include <boost/filesystem.hpp> +#include <boost/function.hpp> #include <boost/interprocess/sync/file_lock.hpp> #include <boost/thread.hpp> #include <openssl/crypto.h> @@ -187,7 +191,11 @@ void Shutdown() pwalletMain->Flush(true); #endif #ifndef WIN32 - boost::filesystem::remove(GetPidFile()); + try { + boost::filesystem::remove(GetPidFile()); + } catch (const boost::filesystem::filesystem_error& e) { + LogPrintf("%s: Unable to remove pidfile: %s\n", __func__, e.what()); + } #endif UnregisterAllValidationInterfaces(); #ifdef ENABLE_WALLET @@ -332,7 +340,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet.dat") + " " + _("on startup")); strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), 0)); strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), 1)); - strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), 1)); + strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); strUsage += HelpMessageOpt("-maxtxfee=<amt>", strprintf(_("Maximum total fees to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)"), FormatMoney(maxTxFee))); strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format") + " " + _("on startup")); @@ -360,7 +368,7 @@ std::string HelpMessage(HelpMessageMode mode) if (mode == HMM_BITCOIN_QT) debugCategories += ", qt"; strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " + - _("If <category> is not supplied, output all debugging information.") + _("<category> can be:") + " " + debugCategories + "."); + _("If <category> is not supplied or if <category> = 1, output all debugging information.") + _("<category> can be:") + " " + debugCategories + "."); #ifdef ENABLE_WALLET strUsage += HelpMessageOpt("-gen", strprintf(_("Generate coins (default: %u)"), 0)); strUsage += HelpMessageOpt("-genproclimit=<n>", strprintf(_("Set the number of threads for coin generation if enabled (-1 = all cores, default: %d)"), 1)); @@ -527,7 +535,7 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles) } // -loadblock= - BOOST_FOREACH(boost::filesystem::path &path, vImportFiles) { + BOOST_FOREACH(const boost::filesystem::path& path, vImportFiles) { FILE *file = fopen(path.string().c_str(), "rb"); if (file) { CImportingNow imp; @@ -564,7 +572,7 @@ bool InitSanityCheck(void) /** Initialize bitcoin. * @pre Parameters should be parsed and config file should be read. */ -bool AppInit2(boost::thread_group& threadGroup) +bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) { // ********************************************************* Step 1: setup #ifdef _MSC_VER @@ -660,6 +668,10 @@ bool AppInit2(boost::thread_group& threadGroup) // to protect privacy, do not listen by default if a default proxy server is specified if (SoftSetBoolArg("-listen", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__); + // to protect privacy, do not use UPNP when a proxy is set. The user may still specify -listen=1 + // to listen locally, so don't rely on this happening through -listen below. + if (SoftSetBoolArg("-upnp", false)) + LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__); // to protect privacy, do not discover addresses by default if (SoftSetBoolArg("-discover", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -discover=0\n", __func__); @@ -775,12 +787,6 @@ bool AppInit2(boost::thread_group& threadGroup) if (nConnectTimeout <= 0) nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; - // Continue to put "/P2SH/" in the coinbase to monitor - // BIP16 support. - // This can be removed eventually... - const char* pszP2SH = "/P2SH/"; - COINBASE_FLAGS << std::vector<unsigned char>(pszP2SH, pszP2SH+strlen(pszP2SH)); - // Fee-per-kilobyte amount considered the same as "free" // If you are mining, be careful setting this: // if you set it to zero then @@ -833,14 +839,14 @@ bool AppInit2(boost::thread_group& threadGroup) mapArgs["-maxtxfee"], ::minRelayTxFee.ToString())); } } - nTxConfirmTarget = GetArg("-txconfirmtarget", 1); - bSpendZeroConfChange = GetArg("-spendzeroconfchange", true); - fSendFreeTransactions = GetArg("-sendfreetransactions", false); + nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); + bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", true); + fSendFreeTransactions = GetBoolArg("-sendfreetransactions", false); std::string strWalletFile = GetArg("-wallet", "wallet.dat"); #endif // ENABLE_WALLET - fIsBareMultisigStd = GetArg("-permitbaremultisig", true) != 0; + fIsBareMultisigStd = GetBoolArg("-permitbaremultisig", true); nMaxDatacarrierBytes = GetArg("-datacarriersize", nMaxDatacarrierBytes); // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log @@ -862,9 +868,15 @@ bool AppInit2(boost::thread_group& threadGroup) boost::filesystem::path pathLockFile = GetDataDir() / ".lock"; FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist. if (file) fclose(file); - static boost::interprocess::file_lock lock(pathLockFile.string().c_str()); - if (!lock.try_lock()) - return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin Core is probably already running."), strDataDir)); + + try { + static boost::interprocess::file_lock lock(pathLockFile.string().c_str()); + if (!lock.try_lock()) + return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin Core is probably already running."), strDataDir)); + } catch(const boost::interprocess::interprocess_exception& e) { + return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin Core is probably already running.") + " %s.", strDataDir, e.what())); + } + #ifndef WIN32 CreatePidFile(GetPidFile(), getpid()); #endif @@ -890,6 +902,10 @@ bool AppInit2(boost::thread_group& threadGroup) threadGroup.create_thread(&ThreadScriptCheck); } + // Start the lightweight task scheduler thread + CScheduler::Function serviceLoop = boost::bind(&CScheduler::serviceQueue, &scheduler); + threadGroup.create_thread(boost::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop)); + /* Start the RPC server already. It will be started in "warmup" mode * and not really process calls already (but it will signify connections * that the server is there and will be ready later). Warmup mode will @@ -913,15 +929,15 @@ bool AppInit2(boost::thread_group& threadGroup) std::string warningString; std::string errorString; - + if (!CWallet::Verify(strWalletFile, warningString, errorString)) return false; - + if (!warningString.empty()) InitWarning(warningString); if (!errorString.empty()) return InitError(warningString); - + } // (!fDisableWallet) #endif // ENABLE_WALLET // ********************************************************* Step 6: network initialization @@ -930,7 +946,7 @@ bool AppInit2(boost::thread_group& threadGroup) if (mapArgs.count("-onlynet")) { std::set<enum Network> nets; - BOOST_FOREACH(std::string snet, mapMultiArgs["-onlynet"]) { + BOOST_FOREACH(const std::string& snet, mapMultiArgs["-onlynet"]) { enum Network net = ParseNetwork(snet); if (net == NET_UNROUTABLE) return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet)); @@ -955,7 +971,7 @@ bool AppInit2(boost::thread_group& threadGroup) proxyType addrProxy; bool fProxy = false; if (mapArgs.count("-proxy")) { - addrProxy = proxyType(CService(mapArgs["-proxy"], 9050), GetArg("-proxyrandomize", true)); + addrProxy = proxyType(CService(mapArgs["-proxy"], 9050), GetBoolArg("-proxyrandomize", true)); if (!addrProxy.IsValid()) return InitError(strprintf(_("Invalid -proxy address: '%s'"), mapArgs["-proxy"])); @@ -972,7 +988,7 @@ bool AppInit2(boost::thread_group& threadGroup) if (!mapArgs.count("-onion")) addrOnion = addrProxy; else - addrOnion = proxyType(CService(mapArgs["-onion"], 9050), GetArg("-proxyrandomize", true)); + addrOnion = proxyType(CService(mapArgs["-onion"], 9050), GetBoolArg("-proxyrandomize", true)); if (!addrOnion.IsValid()) return InitError(strprintf(_("Invalid -onion address: '%s'"), mapArgs["-onion"])); SetProxy(NET_TOR, addrOnion); @@ -987,13 +1003,13 @@ bool AppInit2(boost::thread_group& threadGroup) bool fBound = false; if (fListen) { if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) { - BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) { + BOOST_FOREACH(const std::string& strBind, mapMultiArgs["-bind"]) { CService addrBind; if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind)); fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR)); } - BOOST_FOREACH(std::string strBind, mapMultiArgs["-whitebind"]) { + BOOST_FOREACH(const std::string& strBind, mapMultiArgs["-whitebind"]) { CService addrBind; if (!Lookup(strBind.c_str(), addrBind, 0, false)) return InitError(strprintf(_("Cannot resolve -whitebind address: '%s'"), strBind)); @@ -1013,7 +1029,7 @@ bool AppInit2(boost::thread_group& threadGroup) } if (mapArgs.count("-externalip")) { - BOOST_FOREACH(string strAddr, mapMultiArgs["-externalip"]) { + BOOST_FOREACH(const std::string& strAddr, mapMultiArgs["-externalip"]) { CService addrLocal(strAddr, GetListenPort(), fNameLookup); if (!addrLocal.IsValid()) return InitError(strprintf(_("Cannot resolve -externalip address: '%s'"), strAddr)); @@ -1021,7 +1037,7 @@ bool AppInit2(boost::thread_group& threadGroup) } } - BOOST_FOREACH(string strDest, mapMultiArgs["-seednode"]) + BOOST_FOREACH(const std::string& strDest, mapMultiArgs["-seednode"]) AddOneShot(strDest); // ********************************************************* Step 7: load block chain @@ -1056,18 +1072,20 @@ bool AppInit2(boost::thread_group& threadGroup) } // cache size calculations - size_t nTotalCache = (GetArg("-dbcache", nDefaultDbCache) << 20); - if (nTotalCache < (nMinDbCache << 20)) - nTotalCache = (nMinDbCache << 20); // total cache cannot be less than nMinDbCache - else if (nTotalCache > (nMaxDbCache << 20)) - nTotalCache = (nMaxDbCache << 20); // total cache cannot be greater than nMaxDbCache - size_t nBlockTreeDBCache = nTotalCache / 8; + int64_t nTotalCache = (GetArg("-dbcache", nDefaultDbCache) << 20); + nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache + nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greated than nMaxDbcache + int64_t nBlockTreeDBCache = nTotalCache / 8; if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false)) nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB nTotalCache -= nBlockTreeDBCache; - size_t nCoinDBCache = nTotalCache / 2; // use half of the remaining cache for coindb cache + int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache nTotalCache -= nCoinDBCache; - nCoinCacheSize = nTotalCache / 300; // coins in memory require around 300 bytes + nCoinCacheUsage = nTotalCache; // the rest goes to in-memory cache + LogPrintf("Cache configuration:\n"); + LogPrintf("* Using %.1fMiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024)); + LogPrintf("* Using %.1fMiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024)); + LogPrintf("* Using %.1fMiB for in-memory UTXO set\n", nCoinCacheUsage * (1.0 / 1024 / 1024)); bool fLoaded = false; while (!fLoaded) { @@ -1328,7 +1346,7 @@ bool AppInit2(boost::thread_group& threadGroup) pwalletMain->SetBroadcastTransactions(GetBoolArg("-walletbroadcast", true)); } // (!fDisableWallet) #else // ENABLE_WALLET - LogPrintf("No wallet compiled in!\n"); + LogPrintf("No wallet support compiled in!\n"); #endif // !ENABLE_WALLET // ********************************************************* Step 9: import blocks @@ -1344,7 +1362,7 @@ bool AppInit2(boost::thread_group& threadGroup) std::vector<boost::filesystem::path> vImportFiles; if (mapArgs.count("-loadblock")) { - BOOST_FOREACH(string strFile, mapMultiArgs["-loadblock"]) + BOOST_FOREACH(const std::string& strFile, mapMultiArgs["-loadblock"]) vImportFiles.push_back(strFile); } threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles)); @@ -1373,7 +1391,13 @@ bool AppInit2(boost::thread_group& threadGroup) LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0); #endif - StartNode(threadGroup); + StartNode(threadGroup, scheduler); + + // Monitor the chain, and alert if we get blocks much quicker or slower than expected + int64_t nPowTargetSpacing = Params().GetConsensus().nPowTargetSpacing; + CScheduler::Function f = boost::bind(&PartitionCheck, &IsInitialBlockDownload, + boost::ref(cs_main), boost::cref(chainActive), nPowTargetSpacing); + scheduler.scheduleEvery(f, nPowTargetSpacing); #ifdef ENABLE_WALLET // Generate coins in the background diff --git a/src/init.h b/src/init.h index d451f65be9..dcb2b29360 100644 --- a/src/init.h +++ b/src/init.h @@ -8,6 +8,7 @@ #include <string> +class CScheduler; class CWallet; namespace boost @@ -20,7 +21,7 @@ extern CWallet* pwalletMain; void StartShutdown(); bool ShutdownRequested(); void Shutdown(); -bool AppInit2(boost::thread_group& threadGroup); +bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler); /** The help message mode determines what help message to show */ enum HelpMessageMode { diff --git a/src/json/LICENSE.txt b/src/json/LICENSE.txt deleted file mode 100644 index 797d5363b3..0000000000 --- a/src/json/LICENSE.txt +++ /dev/null @@ -1,24 +0,0 @@ -The MIT License - -Copyright (c) 2007 - 2009 John W. Wilkinson - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/json/json_spirit.h b/src/json/json_spirit.h deleted file mode 100644 index ac1879d5b3..0000000000 --- a/src/json/json_spirit.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef JSON_SPIRIT -#define JSON_SPIRIT - -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -#include "json_spirit_value.h" -#include "json_spirit_reader.h" -#include "json_spirit_writer.h" -#include "json_spirit_utils.h" - -#endif diff --git a/src/json/json_spirit_error_position.h b/src/json/json_spirit_error_position.h deleted file mode 100644 index 17208507df..0000000000 --- a/src/json/json_spirit_error_position.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef JSON_SPIRIT_ERROR_POSITION -#define JSON_SPIRIT_ERROR_POSITION - -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -#include <string> - -namespace json_spirit -{ - // An Error_position exception is thrown by the "read_or_throw" functions below on finding an error. - // Note the "read_or_throw" functions are around 3 times slower than the standard functions "read" - // functions that return a bool. - // - struct Error_position - { - Error_position(); - Error_position( unsigned int line, unsigned int column, const std::string& reason ); - bool operator==( const Error_position& lhs ) const; - unsigned int line_; - unsigned int column_; - std::string reason_; - }; - - inline Error_position::Error_position() - : line_( 0 ) - , column_( 0 ) - { - } - - inline Error_position::Error_position( unsigned int line, unsigned int column, const std::string& reason ) - : line_( line ) - , column_( column ) - , reason_( reason ) - { - } - - inline bool Error_position::operator==( const Error_position& lhs ) const - { - if( this == &lhs ) return true; - - return ( reason_ == lhs.reason_ ) && - ( line_ == lhs.line_ ) && - ( column_ == lhs.column_ ); -} -} - -#endif diff --git a/src/json/json_spirit_reader.cpp b/src/json/json_spirit_reader.cpp deleted file mode 100644 index aa4f637226..0000000000 --- a/src/json/json_spirit_reader.cpp +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#include "json_spirit_reader.h" -#include "json_spirit_reader_template.h" - -using namespace json_spirit; - -bool json_spirit::read( const std::string& s, Value& value ) -{ - return read_string( s, value ); -} - -void json_spirit::read_or_throw( const std::string& s, Value& value ) -{ - read_string_or_throw( s, value ); -} - -bool json_spirit::read( std::istream& is, Value& value ) -{ - return read_stream( is, value ); -} - -void json_spirit::read_or_throw( std::istream& is, Value& value ) -{ - read_stream_or_throw( is, value ); -} - -bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ) -{ - return read_range( begin, end, value ); -} - -void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ) -{ - begin = read_range_or_throw( begin, end, value ); -} - -#ifndef BOOST_NO_STD_WSTRING - -bool json_spirit::read( const std::wstring& s, wValue& value ) -{ - return read_string( s, value ); -} - -void json_spirit::read_or_throw( const std::wstring& s, wValue& value ) -{ - read_string_or_throw( s, value ); -} - -bool json_spirit::read( std::wistream& is, wValue& value ) -{ - return read_stream( is, value ); -} - -void json_spirit::read_or_throw( std::wistream& is, wValue& value ) -{ - read_stream_or_throw( is, value ); -} - -bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ) -{ - return read_range( begin, end, value ); -} - -void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ) -{ - begin = read_range_or_throw( begin, end, value ); -} - -#endif - -bool json_spirit::read( const std::string& s, mValue& value ) -{ - return read_string( s, value ); -} - -void json_spirit::read_or_throw( const std::string& s, mValue& value ) -{ - read_string_or_throw( s, value ); -} - -bool json_spirit::read( std::istream& is, mValue& value ) -{ - return read_stream( is, value ); -} - -void json_spirit::read_or_throw( std::istream& is, mValue& value ) -{ - read_stream_or_throw( is, value ); -} - -bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ) -{ - return read_range( begin, end, value ); -} - -void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ) -{ - begin = read_range_or_throw( begin, end, value ); -} - -#ifndef BOOST_NO_STD_WSTRING - -bool json_spirit::read( const std::wstring& s, wmValue& value ) -{ - return read_string( s, value ); -} - -void json_spirit::read_or_throw( const std::wstring& s, wmValue& value ) -{ - read_string_or_throw( s, value ); -} - -bool json_spirit::read( std::wistream& is, wmValue& value ) -{ - return read_stream( is, value ); -} - -void json_spirit::read_or_throw( std::wistream& is, wmValue& value ) -{ - read_stream_or_throw( is, value ); -} - -bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ) -{ - return read_range( begin, end, value ); -} - -void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ) -{ - begin = read_range_or_throw( begin, end, value ); -} - -#endif diff --git a/src/json/json_spirit_reader.h b/src/json/json_spirit_reader.h deleted file mode 100644 index 96494a9789..0000000000 --- a/src/json/json_spirit_reader.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef JSON_SPIRIT_READER -#define JSON_SPIRIT_READER - -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -#include "json_spirit_value.h" -#include "json_spirit_error_position.h" -#include <iostream> - -namespace json_spirit -{ - // functions to reads a JSON values - - bool read( const std::string& s, Value& value ); - bool read( std::istream& is, Value& value ); - bool read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ); - - void read_or_throw( const std::string& s, Value& value ); - void read_or_throw( std::istream& is, Value& value ); - void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value ); - -#ifndef BOOST_NO_STD_WSTRING - - bool read( const std::wstring& s, wValue& value ); - bool read( std::wistream& is, wValue& value ); - bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ); - - void read_or_throw( const std::wstring& s, wValue& value ); - void read_or_throw( std::wistream& is, wValue& value ); - void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value ); - -#endif - - bool read( const std::string& s, mValue& value ); - bool read( std::istream& is, mValue& value ); - bool read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ); - - void read_or_throw( const std::string& s, mValue& value ); - void read_or_throw( std::istream& is, mValue& value ); - void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value ); - -#ifndef BOOST_NO_STD_WSTRING - - bool read( const std::wstring& s, wmValue& value ); - bool read( std::wistream& is, wmValue& value ); - bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ); - - void read_or_throw( const std::wstring& s, wmValue& value ); - void read_or_throw( std::wistream& is, wmValue& value ); - void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value ); - -#endif -} - -#endif diff --git a/src/json/json_spirit_reader_template.h b/src/json/json_spirit_reader_template.h deleted file mode 100644 index 46f5892f62..0000000000 --- a/src/json/json_spirit_reader_template.h +++ /dev/null @@ -1,612 +0,0 @@ -#ifndef JSON_SPIRIT_READER_TEMPLATE -#define JSON_SPIRIT_READER_TEMPLATE - -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#include "json_spirit_value.h" -#include "json_spirit_error_position.h" - -//#define BOOST_SPIRIT_THREADSAFE // uncomment for multithreaded use, requires linking to boost.thread - -#include <boost/bind.hpp> -#include <boost/function.hpp> -#include <boost/version.hpp> - -#if BOOST_VERSION >= 103800 - #include <boost/spirit/include/classic_core.hpp> - #include <boost/spirit/include/classic_confix.hpp> - #include <boost/spirit/include/classic_escape_char.hpp> - #include <boost/spirit/include/classic_multi_pass.hpp> - #include <boost/spirit/include/classic_position_iterator.hpp> - #define spirit_namespace boost::spirit::classic -#else - #include <boost/spirit/core.hpp> - #include <boost/spirit/utility/confix.hpp> - #include <boost/spirit/utility/escape_char.hpp> - #include <boost/spirit/iterator/multi_pass.hpp> - #include <boost/spirit/iterator/position_iterator.hpp> - #define spirit_namespace boost::spirit -#endif - -namespace json_spirit -{ - const spirit_namespace::int_parser < int64_t > int64_p = spirit_namespace::int_parser < int64_t >(); - const spirit_namespace::uint_parser< uint64_t > uint64_p = spirit_namespace::uint_parser< uint64_t >(); - - template< class Iter_type > - bool is_eq( Iter_type first, Iter_type last, const char* c_str ) - { - for( Iter_type i = first; i != last; ++i, ++c_str ) - { - if( *c_str == 0 ) return false; - - if( *i != *c_str ) return false; - } - - return true; - } - - template< class Char_type > - Char_type hex_to_num( const Char_type c ) - { - if( ( c >= '0' ) && ( c <= '9' ) ) return c - '0'; - if( ( c >= 'a' ) && ( c <= 'f' ) ) return c - 'a' + 10; - if( ( c >= 'A' ) && ( c <= 'F' ) ) return c - 'A' + 10; - return 0; - } - - template< class Char_type, class Iter_type > - Char_type hex_str_to_char( Iter_type& begin ) - { - const Char_type c1( *( ++begin ) ); - const Char_type c2( *( ++begin ) ); - - return ( hex_to_num( c1 ) << 4 ) + hex_to_num( c2 ); - } - - template< class Char_type, class Iter_type > - Char_type unicode_str_to_char( Iter_type& begin ) - { - const Char_type c1( *( ++begin ) ); - const Char_type c2( *( ++begin ) ); - const Char_type c3( *( ++begin ) ); - const Char_type c4( *( ++begin ) ); - - return ( hex_to_num( c1 ) << 12 ) + - ( hex_to_num( c2 ) << 8 ) + - ( hex_to_num( c3 ) << 4 ) + - hex_to_num( c4 ); - } - - template< class String_type > - void append_esc_char_and_incr_iter( String_type& s, - typename String_type::const_iterator& begin, - typename String_type::const_iterator end ) - { - typedef typename String_type::value_type Char_type; - - const Char_type c2( *begin ); - - switch( c2 ) - { - case 't': s += '\t'; break; - case 'b': s += '\b'; break; - case 'f': s += '\f'; break; - case 'n': s += '\n'; break; - case 'r': s += '\r'; break; - case '\\': s += '\\'; break; - case '/': s += '/'; break; - case '"': s += '"'; break; - case 'x': - { - if( end - begin >= 3 ) // expecting "xHH..." - { - s += hex_str_to_char< Char_type >( begin ); - } - break; - } - case 'u': - { - if( end - begin >= 5 ) // expecting "uHHHH..." - { - s += unicode_str_to_char< Char_type >( begin ); - } - break; - } - } - } - - template< class String_type > - String_type substitute_esc_chars( typename String_type::const_iterator begin, - typename String_type::const_iterator end ) - { - typedef typename String_type::const_iterator Iter_type; - - if( end - begin < 2 ) return String_type( begin, end ); - - String_type result; - - result.reserve( end - begin ); - - const Iter_type end_minus_1( end - 1 ); - - Iter_type substr_start = begin; - Iter_type i = begin; - - for( ; i < end_minus_1; ++i ) - { - if( *i == '\\' ) - { - result.append( substr_start, i ); - - ++i; // skip the '\' - - append_esc_char_and_incr_iter( result, i, end ); - - substr_start = i + 1; - } - } - - result.append( substr_start, end ); - - return result; - } - - template< class String_type > - String_type get_str_( typename String_type::const_iterator begin, - typename String_type::const_iterator end ) - { - assert( end - begin >= 2 ); - - typedef typename String_type::const_iterator Iter_type; - - Iter_type str_without_quotes( ++begin ); - Iter_type end_without_quotes( --end ); - - return substitute_esc_chars< String_type >( str_without_quotes, end_without_quotes ); - } - - inline std::string get_str( std::string::const_iterator begin, std::string::const_iterator end ) - { - return get_str_< std::string >( begin, end ); - } - - inline std::wstring get_str( std::wstring::const_iterator begin, std::wstring::const_iterator end ) - { - return get_str_< std::wstring >( begin, end ); - } - - template< class String_type, class Iter_type > - String_type get_str( Iter_type begin, Iter_type end ) - { - const String_type tmp( begin, end ); // convert multipass iterators to string iterators - - return get_str( tmp.begin(), tmp.end() ); - } - - // this class's methods get called by the spirit parse resulting - // in the creation of a JSON object or array - // - // NB Iter_type could be a std::string iterator, wstring iterator, a position iterator or a multipass iterator - // - template< class Value_type, class Iter_type > - class Semantic_actions - { - public: - - typedef typename Value_type::Config_type Config_type; - typedef typename Config_type::String_type String_type; - typedef typename Config_type::Object_type Object_type; - typedef typename Config_type::Array_type Array_type; - typedef typename String_type::value_type Char_type; - - Semantic_actions( Value_type& value ) - : value_( value ) - , current_p_( 0 ) - { - } - - void begin_obj( Char_type c ) - { - assert( c == '{' ); - - begin_compound< Object_type >(); - } - - void end_obj( Char_type c ) - { - assert( c == '}' ); - - end_compound(); - } - - void begin_array( Char_type c ) - { - assert( c == '[' ); - - begin_compound< Array_type >(); - } - - void end_array( Char_type c ) - { - assert( c == ']' ); - - end_compound(); - } - - void new_name( Iter_type begin, Iter_type end ) - { - assert( current_p_->type() == obj_type ); - - name_ = get_str< String_type >( begin, end ); - } - - void new_str( Iter_type begin, Iter_type end ) - { - add_to_current( get_str< String_type >( begin, end ) ); - } - - void new_true( Iter_type begin, Iter_type end ) - { - assert( is_eq( begin, end, "true" ) ); - - add_to_current( true ); - } - - void new_false( Iter_type begin, Iter_type end ) - { - assert( is_eq( begin, end, "false" ) ); - - add_to_current( false ); - } - - void new_null( Iter_type begin, Iter_type end ) - { - assert( is_eq( begin, end, "null" ) ); - - add_to_current( Value_type() ); - } - - void new_int( int64_t i ) - { - add_to_current( i ); - } - - void new_uint64( uint64_t ui ) - { - add_to_current( ui ); - } - - void new_real( double d ) - { - add_to_current( d ); - } - - private: - - Semantic_actions& operator=( const Semantic_actions& ); - // to prevent "assignment operator could not be generated" warning - - Value_type* add_first( const Value_type& value ) - { - assert( current_p_ == 0 ); - - value_ = value; - current_p_ = &value_; - return current_p_; - } - - template< class Array_or_obj > - void begin_compound() - { - if( current_p_ == 0 ) - { - add_first( Array_or_obj() ); - } - else - { - stack_.push_back( current_p_ ); - - Array_or_obj new_array_or_obj; // avoid copy by building new array or object in place - - current_p_ = add_to_current( new_array_or_obj ); - } - } - - void end_compound() - { - if( current_p_ != &value_ ) - { - current_p_ = stack_.back(); - - stack_.pop_back(); - } - } - - Value_type* add_to_current( const Value_type& value ) - { - if( current_p_ == 0 ) - { - return add_first( value ); - } - else if( current_p_->type() == array_type ) - { - current_p_->get_array().push_back( value ); - - return ¤t_p_->get_array().back(); - } - - assert( current_p_->type() == obj_type ); - - return &Config_type::add( current_p_->get_obj(), name_, value ); - } - - Value_type& value_; // this is the object or array that is being created - Value_type* current_p_; // the child object or array that is currently being constructed - - std::vector< Value_type* > stack_; // previous child objects and arrays - - String_type name_; // of current name/value pair - }; - - template< typename Iter_type > - void throw_error( spirit_namespace::position_iterator< Iter_type > i, const std::string& reason ) - { - throw Error_position( i.get_position().line, i.get_position().column, reason ); - } - - template< typename Iter_type > - void throw_error( Iter_type i, const std::string& reason ) - { - throw reason; - } - - // the spirit grammer - // - template< class Value_type, class Iter_type > - class Json_grammer : public spirit_namespace::grammar< Json_grammer< Value_type, Iter_type > > - { - public: - - typedef Semantic_actions< Value_type, Iter_type > Semantic_actions_t; - - Json_grammer( Semantic_actions_t& semantic_actions ) - : actions_( semantic_actions ) - { - } - - static void throw_not_value( Iter_type begin, Iter_type end ) - { - throw_error( begin, "not a value" ); - } - - static void throw_not_array( Iter_type begin, Iter_type end ) - { - throw_error( begin, "not an array" ); - } - - static void throw_not_object( Iter_type begin, Iter_type end ) - { - throw_error( begin, "not an object" ); - } - - static void throw_not_pair( Iter_type begin, Iter_type end ) - { - throw_error( begin, "not a pair" ); - } - - static void throw_not_colon( Iter_type begin, Iter_type end ) - { - throw_error( begin, "no colon in pair" ); - } - - static void throw_not_string( Iter_type begin, Iter_type end ) - { - throw_error( begin, "not a string" ); - } - - template< typename ScannerT > - class definition - { - public: - - definition( const Json_grammer& self ) - { - using namespace spirit_namespace; - - typedef typename Value_type::String_type::value_type Char_type; - - // first we convert the semantic action class methods to functors with the - // parameter signature expected by spirit - - typedef boost::function< void( Char_type ) > Char_action; - typedef boost::function< void( Iter_type, Iter_type ) > Str_action; - typedef boost::function< void( double ) > Real_action; - typedef boost::function< void( int64_t ) > Int_action; - typedef boost::function< void( uint64_t ) > Uint64_action; - - Char_action begin_obj ( boost::bind( &Semantic_actions_t::begin_obj, &self.actions_, _1 ) ); - Char_action end_obj ( boost::bind( &Semantic_actions_t::end_obj, &self.actions_, _1 ) ); - Char_action begin_array( boost::bind( &Semantic_actions_t::begin_array, &self.actions_, _1 ) ); - Char_action end_array ( boost::bind( &Semantic_actions_t::end_array, &self.actions_, _1 ) ); - Str_action new_name ( boost::bind( &Semantic_actions_t::new_name, &self.actions_, _1, _2 ) ); - Str_action new_str ( boost::bind( &Semantic_actions_t::new_str, &self.actions_, _1, _2 ) ); - Str_action new_true ( boost::bind( &Semantic_actions_t::new_true, &self.actions_, _1, _2 ) ); - Str_action new_false ( boost::bind( &Semantic_actions_t::new_false, &self.actions_, _1, _2 ) ); - Str_action new_null ( boost::bind( &Semantic_actions_t::new_null, &self.actions_, _1, _2 ) ); - Real_action new_real ( boost::bind( &Semantic_actions_t::new_real, &self.actions_, _1 ) ); - Int_action new_int ( boost::bind( &Semantic_actions_t::new_int, &self.actions_, _1 ) ); - Uint64_action new_uint64 ( boost::bind( &Semantic_actions_t::new_uint64, &self.actions_, _1 ) ); - - // actual grammer - - json_ - = value_ | eps_p[ &throw_not_value ] - ; - - value_ - = string_[ new_str ] - | number_ - | object_ - | array_ - | str_p( "true" ) [ new_true ] - | str_p( "false" )[ new_false ] - | str_p( "null" ) [ new_null ] - ; - - object_ - = ch_p('{')[ begin_obj ] - >> !members_ - >> ( ch_p('}')[ end_obj ] | eps_p[ &throw_not_object ] ) - ; - - members_ - = pair_ >> *( ',' >> pair_ ) - ; - - pair_ - = string_[ new_name ] - >> ( ':' | eps_p[ &throw_not_colon ] ) - >> ( value_ | eps_p[ &throw_not_value ] ) - ; - - array_ - = ch_p('[')[ begin_array ] - >> !elements_ - >> ( ch_p(']')[ end_array ] | eps_p[ &throw_not_array ] ) - ; - - elements_ - = value_ >> *( ',' >> value_ ) - ; - - string_ - = lexeme_d // this causes white space inside a string to be retained - [ - confix_p - ( - '"', - *lex_escape_ch_p, - '"' - ) - ] - ; - - number_ - = strict_real_p[ new_real ] - | int64_p [ new_int ] - | uint64_p [ new_uint64 ] - ; - } - - spirit_namespace::rule< ScannerT > json_, object_, members_, pair_, array_, elements_, value_, string_, number_; - - const spirit_namespace::rule< ScannerT >& start() const { return json_; } - }; - - private: - - Json_grammer& operator=( const Json_grammer& ); // to prevent "assignment operator could not be generated" warning - - Semantic_actions_t& actions_; - }; - - template< class Iter_type, class Value_type > - Iter_type read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value ) - { - Semantic_actions< Value_type, Iter_type > semantic_actions( value ); - - const spirit_namespace::parse_info< Iter_type > info = - spirit_namespace::parse( begin, end, - Json_grammer< Value_type, Iter_type >( semantic_actions ), - spirit_namespace::space_p ); - - if( !info.hit ) - { - assert( false ); // in theory exception should already have been thrown - throw_error( info.stop, "error" ); - } - - return info.stop; - } - - template< class Iter_type, class Value_type > - void add_posn_iter_and_read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value ) - { - typedef spirit_namespace::position_iterator< Iter_type > Posn_iter_t; - - const Posn_iter_t posn_begin( begin, end ); - const Posn_iter_t posn_end( end, end ); - - read_range_or_throw( posn_begin, posn_end, value ); - } - - template< class Iter_type, class Value_type > - bool read_range( Iter_type& begin, Iter_type end, Value_type& value ) - { - try - { - begin = read_range_or_throw( begin, end, value ); - - return true; - } - catch( ... ) - { - return false; - } - } - - template< class String_type, class Value_type > - void read_string_or_throw( const String_type& s, Value_type& value ) - { - add_posn_iter_and_read_range_or_throw( s.begin(), s.end(), value ); - } - - template< class String_type, class Value_type > - bool read_string( const String_type& s, Value_type& value ) - { - typename String_type::const_iterator begin = s.begin(); - - return read_range( begin, s.end(), value ); - } - - template< class Istream_type > - struct Multi_pass_iters - { - typedef typename Istream_type::char_type Char_type; - typedef std::istream_iterator< Char_type, Char_type > istream_iter; - typedef spirit_namespace::multi_pass< istream_iter > Mp_iter; - - Multi_pass_iters( Istream_type& is ) - { - is.unsetf( std::ios::skipws ); - - begin_ = spirit_namespace::make_multi_pass( istream_iter( is ) ); - end_ = spirit_namespace::make_multi_pass( istream_iter() ); - } - - Mp_iter begin_; - Mp_iter end_; - }; - - template< class Istream_type, class Value_type > - bool read_stream( Istream_type& is, Value_type& value ) - { - Multi_pass_iters< Istream_type > mp_iters( is ); - - return read_range( mp_iters.begin_, mp_iters.end_, value ); - } - - template< class Istream_type, class Value_type > - void read_stream_or_throw( Istream_type& is, Value_type& value ) - { - const Multi_pass_iters< Istream_type > mp_iters( is ); - - add_posn_iter_and_read_range_or_throw( mp_iters.begin_, mp_iters.end_, value ); - } -} - -#endif diff --git a/src/json/json_spirit_stream_reader.h b/src/json/json_spirit_stream_reader.h deleted file mode 100644 index 7e59c9adc2..0000000000 --- a/src/json/json_spirit_stream_reader.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef JSON_SPIRIT_READ_STREAM -#define JSON_SPIRIT_READ_STREAM - -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -#include "json_spirit_reader_template.h" - -namespace json_spirit -{ - // these classes allows you to read multiple top level contiguous values from a stream, - // the normal stream read functions have a bug that prevent multiple top level values - // from being read unless they are separated by spaces - - template< class Istream_type, class Value_type > - class Stream_reader - { - public: - - Stream_reader( Istream_type& is ) - : iters_( is ) - { - } - - bool read_next( Value_type& value ) - { - return read_range( iters_.begin_, iters_.end_, value ); - } - - private: - - typedef Multi_pass_iters< Istream_type > Mp_iters; - - Mp_iters iters_; - }; - - template< class Istream_type, class Value_type > - class Stream_reader_thrower - { - public: - - Stream_reader_thrower( Istream_type& is ) - : iters_( is ) - , posn_begin_( iters_.begin_, iters_.end_ ) - , posn_end_( iters_.end_, iters_.end_ ) - { - } - - void read_next( Value_type& value ) - { - posn_begin_ = read_range_or_throw( posn_begin_, posn_end_, value ); - } - - private: - - typedef Multi_pass_iters< Istream_type > Mp_iters; - typedef spirit_namespace::position_iterator< typename Mp_iters::Mp_iter > Posn_iter_t; - - Mp_iters iters_; - Posn_iter_t posn_begin_, posn_end_; - }; -} - -#endif diff --git a/src/json/json_spirit_utils.h b/src/json/json_spirit_utils.h deleted file mode 100644 index 553e3b96a4..0000000000 --- a/src/json/json_spirit_utils.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef JSON_SPIRIT_UTILS -#define JSON_SPIRIT_UTILS - -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -#include "json_spirit_value.h" -#include <map> - -namespace json_spirit -{ - template< class Obj_t, class Map_t > - void obj_to_map( const Obj_t& obj, Map_t& mp_obj ) - { - mp_obj.clear(); - - for( typename Obj_t::const_iterator i = obj.begin(); i != obj.end(); ++i ) - { - mp_obj[ i->name_ ] = i->value_; - } - } - - template< class Obj_t, class Map_t > - void map_to_obj( const Map_t& mp_obj, Obj_t& obj ) - { - obj.clear(); - - for( typename Map_t::const_iterator i = mp_obj.begin(); i != mp_obj.end(); ++i ) - { - obj.push_back( typename Obj_t::value_type( i->first, i->second ) ); - } - } - - typedef std::map< std::string, Value > Mapped_obj; - -#ifndef BOOST_NO_STD_WSTRING - typedef std::map< std::wstring, wValue > wMapped_obj; -#endif - - template< class Object_type, class String_type > - const typename Object_type::value_type::Value_type& find_value( const Object_type& obj, const String_type& name ) - { - for( typename Object_type::const_iterator i = obj.begin(); i != obj.end(); ++i ) - { - if( i->name_ == name ) - { - return i->value_; - } - } - - return Object_type::value_type::Value_type::null; - } -} - -#endif diff --git a/src/json/json_spirit_value.cpp b/src/json/json_spirit_value.cpp deleted file mode 100644 index 44d2f06a01..0000000000 --- a/src/json/json_spirit_value.cpp +++ /dev/null @@ -1,8 +0,0 @@ -/* Copyright (c) 2007 John W Wilkinson - - This source code can be used for any purpose as long as - this comment is retained. */ - -// json spirit version 2.00 - -#include "json_spirit_value.h" diff --git a/src/json/json_spirit_value.h b/src/json/json_spirit_value.h deleted file mode 100644 index 13cc89210c..0000000000 --- a/src/json/json_spirit_value.h +++ /dev/null @@ -1,534 +0,0 @@ -#ifndef JSON_SPIRIT_VALUE -#define JSON_SPIRIT_VALUE - -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -#include <vector> -#include <map> -#include <string> -#include <cassert> -#include <sstream> -#include <stdexcept> -#include <stdint.h> -#include <boost/config.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/variant.hpp> - -namespace json_spirit -{ - enum Value_type{ obj_type, array_type, str_type, bool_type, int_type, real_type, null_type }; - static const char* Value_type_name[]={"obj", "array", "str", "bool", "int", "real", "null"}; - - template< class Config > // Config determines whether the value uses std::string or std::wstring and - // whether JSON Objects are represented as vectors or maps - class Value_impl - { - public: - - typedef Config Config_type; - typedef typename Config::String_type String_type; - typedef typename Config::Object_type Object; - typedef typename Config::Array_type Array; - typedef typename String_type::const_pointer Const_str_ptr; // eg const char* - - Value_impl(); // creates null value - Value_impl( Const_str_ptr value ); - Value_impl( const String_type& value ); - Value_impl( const Object& value ); - Value_impl( const Array& value ); - Value_impl( bool value ); - Value_impl( int value ); - Value_impl( int64_t value ); - Value_impl( uint64_t value ); - Value_impl( double value ); - - Value_impl( const Value_impl& other ); - - bool operator==( const Value_impl& lhs ) const; - - Value_impl& operator=( const Value_impl& lhs ); - - Value_type type() const; - - bool is_uint64() const; - bool is_null() const; - - const String_type& get_str() const; - const Object& get_obj() const; - const Array& get_array() const; - bool get_bool() const; - int get_int() const; - int64_t get_int64() const; - uint64_t get_uint64() const; - double get_real() const; - - Object& get_obj(); - Array& get_array(); - - template< typename T > T get_value() const; // example usage: int i = value.get_value< int >(); - // or double d = value.get_value< double >(); - - static const Value_impl null; - - private: - - void check_type( const Value_type vtype ) const; - - typedef boost::variant< String_type, - boost::recursive_wrapper< Object >, boost::recursive_wrapper< Array >, - bool, int64_t, double > Variant; - - Value_type type_; - Variant v_; - bool is_uint64_; - }; - - // vector objects - - template< class Config > - struct Pair_impl - { - typedef typename Config::String_type String_type; - typedef typename Config::Value_type Value_type; - - Pair_impl( const String_type& name, const Value_type& value ); - - bool operator==( const Pair_impl& lhs ) const; - - String_type name_; - Value_type value_; - }; - - template< class String > - struct Config_vector - { - typedef String String_type; - typedef Value_impl< Config_vector > Value_type; - typedef Pair_impl < Config_vector > Pair_type; - typedef std::vector< Value_type > Array_type; - typedef std::vector< Pair_type > Object_type; - - static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value ) - { - obj.push_back( Pair_type( name , value ) ); - - return obj.back().value_; - } - - static String_type get_name( const Pair_type& pair ) - { - return pair.name_; - } - - static Value_type get_value( const Pair_type& pair ) - { - return pair.value_; - } - }; - - // typedefs for ASCII - - typedef Config_vector< std::string > Config; - - typedef Config::Value_type Value; - typedef Config::Pair_type Pair; - typedef Config::Object_type Object; - typedef Config::Array_type Array; - - // typedefs for Unicode - -#ifndef BOOST_NO_STD_WSTRING - - typedef Config_vector< std::wstring > wConfig; - - typedef wConfig::Value_type wValue; - typedef wConfig::Pair_type wPair; - typedef wConfig::Object_type wObject; - typedef wConfig::Array_type wArray; -#endif - - // map objects - - template< class String > - struct Config_map - { - typedef String String_type; - typedef Value_impl< Config_map > Value_type; - typedef std::vector< Value_type > Array_type; - typedef std::map< String_type, Value_type > Object_type; - typedef typename Object_type::value_type Pair_type; - - static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value ) - { - return obj[ name ] = value; - } - - static String_type get_name( const Pair_type& pair ) - { - return pair.first; - } - - static Value_type get_value( const Pair_type& pair ) - { - return pair.second; - } - }; - - // typedefs for ASCII - - typedef Config_map< std::string > mConfig; - - typedef mConfig::Value_type mValue; - typedef mConfig::Object_type mObject; - typedef mConfig::Array_type mArray; - - // typedefs for Unicode - -#ifndef BOOST_NO_STD_WSTRING - - typedef Config_map< std::wstring > wmConfig; - - typedef wmConfig::Value_type wmValue; - typedef wmConfig::Object_type wmObject; - typedef wmConfig::Array_type wmArray; - -#endif - - /////////////////////////////////////////////////////////////////////////////////////////////// - // - // implementation - - template< class Config > - const Value_impl< Config > Value_impl< Config >::null; - - template< class Config > - Value_impl< Config >::Value_impl() - : type_( null_type ) - , is_uint64_( false ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( const Const_str_ptr value ) - : type_( str_type ) - , v_( String_type( value ) ) - , is_uint64_( false ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( const String_type& value ) - : type_( str_type ) - , v_( value ) - , is_uint64_( false ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( const Object& value ) - : type_( obj_type ) - , v_( value ) - , is_uint64_( false ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( const Array& value ) - : type_( array_type ) - , v_( value ) - , is_uint64_( false ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( bool value ) - : type_( bool_type ) - , v_( value ) - , is_uint64_( false ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( int value ) - : type_( int_type ) - , v_( static_cast< int64_t >( value ) ) - , is_uint64_( false ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( int64_t value ) - : type_( int_type ) - , v_( value ) - , is_uint64_( false ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( uint64_t value ) - : type_( int_type ) - , v_( static_cast< int64_t >( value ) ) - , is_uint64_( true ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( double value ) - : type_( real_type ) - , v_( value ) - , is_uint64_( false ) - { - } - - template< class Config > - Value_impl< Config >::Value_impl( const Value_impl< Config >& other ) - : type_( other.type() ) - , v_( other.v_ ) - , is_uint64_( other.is_uint64_ ) - { - } - - template< class Config > - Value_impl< Config >& Value_impl< Config >::operator=( const Value_impl& lhs ) - { - Value_impl tmp( lhs ); - - std::swap( type_, tmp.type_ ); - std::swap( v_, tmp.v_ ); - std::swap( is_uint64_, tmp.is_uint64_ ); - - return *this; - } - - template< class Config > - bool Value_impl< Config >::operator==( const Value_impl& lhs ) const - { - if( this == &lhs ) return true; - - if( type() != lhs.type() ) return false; - - return v_ == lhs.v_; - } - - template< class Config > - Value_type Value_impl< Config >::type() const - { - return type_; - } - - template< class Config > - bool Value_impl< Config >::is_uint64() const - { - return is_uint64_; - } - - template< class Config > - bool Value_impl< Config >::is_null() const - { - return type() == null_type; - } - - template< class Config > - void Value_impl< Config >::check_type( const Value_type vtype ) const - { - if( type() != vtype ) - { - std::ostringstream os; - - ///// Bitcoin: Tell the types by name instead of by number - os << "value is type " << Value_type_name[type()] << ", expected " << Value_type_name[vtype]; - - throw std::runtime_error( os.str() ); - } - } - - template< class Config > - const typename Config::String_type& Value_impl< Config >::get_str() const - { - check_type( str_type ); - - return *boost::get< String_type >( &v_ ); - } - - template< class Config > - const typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() const - { - check_type( obj_type ); - - return *boost::get< Object >( &v_ ); - } - - template< class Config > - const typename Value_impl< Config >::Array& Value_impl< Config >::get_array() const - { - check_type( array_type ); - - return *boost::get< Array >( &v_ ); - } - - template< class Config > - bool Value_impl< Config >::get_bool() const - { - check_type( bool_type ); - - return boost::get< bool >( v_ ); - } - - template< class Config > - int Value_impl< Config >::get_int() const - { - check_type( int_type ); - - return static_cast< int >( get_int64() ); - } - - template< class Config > - int64_t Value_impl< Config >::get_int64() const - { - check_type( int_type ); - - return boost::get< int64_t >( v_ ); - } - - template< class Config > - uint64_t Value_impl< Config >::get_uint64() const - { - check_type( int_type ); - - return static_cast< uint64_t >( get_int64() ); - } - - template< class Config > - double Value_impl< Config >::get_real() const - { - if( type() == int_type ) - { - return is_uint64() ? static_cast< double >( get_uint64() ) - : static_cast< double >( get_int64() ); - } - - check_type( real_type ); - - return boost::get< double >( v_ ); - } - - template< class Config > - typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() - { - check_type( obj_type ); - - return *boost::get< Object >( &v_ ); - } - - template< class Config > - typename Value_impl< Config >::Array& Value_impl< Config >::get_array() - { - check_type( array_type ); - - return *boost::get< Array >( &v_ ); - } - - template< class Config > - Pair_impl< Config >::Pair_impl( const String_type& name, const Value_type& value ) - : name_( name ) - , value_( value ) - { - } - - template< class Config > - bool Pair_impl< Config >::operator==( const Pair_impl< Config >& lhs ) const - { - if( this == &lhs ) return true; - - return ( name_ == lhs.name_ ) && ( value_ == lhs.value_ ); - } - - // converts a C string, ie. 8 bit char array, to a string object - // - template < class String_type > - String_type to_str( const char* c_str ) - { - String_type result; - - for( const char* p = c_str; *p != 0; ++p ) - { - result += *p; - } - - return result; - } - - // - - namespace internal_ - { - template< typename T > - struct Type_to_type - { - }; - - template< class Value > - int get_value( const Value& value, Type_to_type< int > ) - { - return value.get_int(); - } - - template< class Value > - int64_t get_value( const Value& value, Type_to_type< int64_t > ) - { - return value.get_int64(); - } - - template< class Value > - uint64_t get_value( const Value& value, Type_to_type< uint64_t > ) - { - return value.get_uint64(); - } - - template< class Value > - double get_value( const Value& value, Type_to_type< double > ) - { - return value.get_real(); - } - - template< class Value > - typename Value::String_type get_value( const Value& value, Type_to_type< typename Value::String_type > ) - { - return value.get_str(); - } - - template< class Value > - typename Value::Array get_value( const Value& value, Type_to_type< typename Value::Array > ) - { - return value.get_array(); - } - - template< class Value > - typename Value::Object get_value( const Value& value, Type_to_type< typename Value::Object > ) - { - return value.get_obj(); - } - - template< class Value > - bool get_value( const Value& value, Type_to_type< bool > ) - { - return value.get_bool(); - } - } - - template< class Config > - template< typename T > - T Value_impl< Config >::get_value() const - { - return internal_::get_value( *this, internal_::Type_to_type< T >() ); - } -} - -#endif diff --git a/src/json/json_spirit_writer.cpp b/src/json/json_spirit_writer.cpp deleted file mode 100644 index d24a632cf3..0000000000 --- a/src/json/json_spirit_writer.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#include "json_spirit_writer.h" -#include "json_spirit_writer_template.h" - -void json_spirit::write( const Value& value, std::ostream& os ) -{ - write_stream( value, os, false ); -} - -void json_spirit::write_formatted( const Value& value, std::ostream& os ) -{ - write_stream( value, os, true ); -} - -std::string json_spirit::write( const Value& value ) -{ - return write_string( value, false ); -} - -std::string json_spirit::write_formatted( const Value& value ) -{ - return write_string( value, true ); -} - -#ifndef BOOST_NO_STD_WSTRING - -void json_spirit::write( const wValue& value, std::wostream& os ) -{ - write_stream( value, os, false ); -} - -void json_spirit::write_formatted( const wValue& value, std::wostream& os ) -{ - write_stream( value, os, true ); -} - -std::wstring json_spirit::write( const wValue& value ) -{ - return write_string( value, false ); -} - -std::wstring json_spirit::write_formatted( const wValue& value ) -{ - return write_string( value, true ); -} - -#endif - -void json_spirit::write( const mValue& value, std::ostream& os ) -{ - write_stream( value, os, false ); -} - -void json_spirit::write_formatted( const mValue& value, std::ostream& os ) -{ - write_stream( value, os, true ); -} - -std::string json_spirit::write( const mValue& value ) -{ - return write_string( value, false ); -} - -std::string json_spirit::write_formatted( const mValue& value ) -{ - return write_string( value, true ); -} - -#ifndef BOOST_NO_STD_WSTRING - -void json_spirit::write( const wmValue& value, std::wostream& os ) -{ - write_stream( value, os, false ); -} - -void json_spirit::write_formatted( const wmValue& value, std::wostream& os ) -{ - write_stream( value, os, true ); -} - -std::wstring json_spirit::write( const wmValue& value ) -{ - return write_string( value, false ); -} - -std::wstring json_spirit::write_formatted( const wmValue& value ) -{ - return write_string( value, true ); -} - -#endif diff --git a/src/json/json_spirit_writer.h b/src/json/json_spirit_writer.h deleted file mode 100644 index 52e14068e7..0000000000 --- a/src/json/json_spirit_writer.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef JSON_SPIRIT_WRITER -#define JSON_SPIRIT_WRITER - -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -#include "json_spirit_value.h" -#include <iostream> - -namespace json_spirit -{ - // functions to convert JSON Values to text, - // the "formatted" versions add whitespace to format the output nicely - - void write ( const Value& value, std::ostream& os ); - void write_formatted( const Value& value, std::ostream& os ); - std::string write ( const Value& value ); - std::string write_formatted( const Value& value ); - -#ifndef BOOST_NO_STD_WSTRING - - void write ( const wValue& value, std::wostream& os ); - void write_formatted( const wValue& value, std::wostream& os ); - std::wstring write ( const wValue& value ); - std::wstring write_formatted( const wValue& value ); - -#endif - - void write ( const mValue& value, std::ostream& os ); - void write_formatted( const mValue& value, std::ostream& os ); - std::string write ( const mValue& value ); - std::string write_formatted( const mValue& value ); - -#ifndef BOOST_NO_STD_WSTRING - - void write ( const wmValue& value, std::wostream& os ); - void write_formatted( const wmValue& value, std::wostream& os ); - std::wstring write ( const wmValue& value ); - std::wstring write_formatted( const wmValue& value ); - -#endif -} - -#endif diff --git a/src/json/json_spirit_writer_template.h b/src/json/json_spirit_writer_template.h deleted file mode 100644 index 6b4978a1ff..0000000000 --- a/src/json/json_spirit_writer_template.h +++ /dev/null @@ -1,249 +0,0 @@ -#ifndef JSON_SPIRIT_WRITER_TEMPLATE -#define JSON_SPIRIT_WRITER_TEMPLATE - -// Copyright John W. Wilkinson 2007 - 2009. -// Distributed under the MIT License, see accompanying file LICENSE.txt - -// json spirit version 4.03 - -#include "json_spirit_value.h" - -#include <cassert> -#include <sstream> -#include <iomanip> - -namespace json_spirit -{ - inline char to_hex_char( unsigned int c ) - { - assert( c <= 0xF ); - - const char ch = static_cast< char >( c ); - - if( ch < 10 ) return '0' + ch; - - return 'A' - 10 + ch; - } - - template< class String_type > - String_type non_printable_to_string( unsigned int c ) - { - // Silence the warning: typedef ‘Char_type’ locally defined but not used [-Wunused-local-typedefs] - // typedef typename String_type::value_type Char_type; - - String_type result( 6, '\\' ); - - result[1] = 'u'; - - result[ 5 ] = to_hex_char( c & 0x000F ); c >>= 4; - result[ 4 ] = to_hex_char( c & 0x000F ); c >>= 4; - result[ 3 ] = to_hex_char( c & 0x000F ); c >>= 4; - result[ 2 ] = to_hex_char( c & 0x000F ); - - return result; - } - - template< typename Char_type, class String_type > - bool add_esc_char( Char_type c, String_type& s ) - { - switch( c ) - { - case '"': s += to_str< String_type >( "\\\"" ); return true; - case '\\': s += to_str< String_type >( "\\\\" ); return true; - case '\b': s += to_str< String_type >( "\\b" ); return true; - case '\f': s += to_str< String_type >( "\\f" ); return true; - case '\n': s += to_str< String_type >( "\\n" ); return true; - case '\r': s += to_str< String_type >( "\\r" ); return true; - case '\t': s += to_str< String_type >( "\\t" ); return true; - } - - return false; - } - - template< class String_type > - String_type add_esc_chars( const String_type& s ) - { - typedef typename String_type::const_iterator Iter_type; - typedef typename String_type::value_type Char_type; - - String_type result; - - const Iter_type end( s.end() ); - - for( Iter_type i = s.begin(); i != end; ++i ) - { - const Char_type c( *i ); - - if( add_esc_char( c, result ) ) continue; - - const wint_t unsigned_c( ( c >= 0 ) ? c : 256 + c ); - - if( iswprint( unsigned_c ) ) - { - result += c; - } - else - { - result += non_printable_to_string< String_type >( unsigned_c ); - } - } - - return result; - } - - // this class generates the JSON text, - // it keeps track of the indentation level etc. - // - template< class Value_type, class Ostream_type > - class Generator - { - typedef typename Value_type::Config_type Config_type; - typedef typename Config_type::String_type String_type; - typedef typename Config_type::Object_type Object_type; - typedef typename Config_type::Array_type Array_type; - typedef typename String_type::value_type Char_type; - typedef typename Object_type::value_type Obj_member_type; - - public: - - Generator( const Value_type& value, Ostream_type& os, bool pretty ) - : os_( os ) - , indentation_level_( 0 ) - , pretty_( pretty ) - { - output( value ); - } - - private: - - void output( const Value_type& value ) - { - switch( value.type() ) - { - case obj_type: output( value.get_obj() ); break; - case array_type: output( value.get_array() ); break; - case str_type: output( value.get_str() ); break; - case bool_type: output( value.get_bool() ); break; - case int_type: output_int( value ); break; - - /// Bitcoin: Added std::fixed and changed precision from 16 to 8 - case real_type: os_ << std::showpoint << std::fixed << std::setprecision(8) - << value.get_real(); break; - - case null_type: os_ << "null"; break; - default: assert( false ); - } - } - - void output( const Object_type& obj ) - { - output_array_or_obj( obj, '{', '}' ); - } - - void output( const Array_type& arr ) - { - output_array_or_obj( arr, '[', ']' ); - } - - void output( const Obj_member_type& member ) - { - output( Config_type::get_name( member ) ); space(); - os_ << ':'; space(); - output( Config_type::get_value( member ) ); - } - - void output_int( const Value_type& value ) - { - if( value.is_uint64() ) - { - os_ << value.get_uint64(); - } - else - { - os_ << value.get_int64(); - } - } - - void output( const String_type& s ) - { - os_ << '"' << add_esc_chars( s ) << '"'; - } - - void output( bool b ) - { - os_ << to_str< String_type >( b ? "true" : "false" ); - } - - template< class T > - void output_array_or_obj( const T& t, Char_type start_char, Char_type end_char ) - { - os_ << start_char; new_line(); - - ++indentation_level_; - - for( typename T::const_iterator i = t.begin(); i != t.end(); ++i ) - { - indent(); output( *i ); - - typename T::const_iterator next = i; - - if( ++next != t.end()) - { - os_ << ','; - } - - new_line(); - } - - --indentation_level_; - - indent(); os_ << end_char; - } - - void indent() - { - if( !pretty_ ) return; - - for( int i = 0; i < indentation_level_; ++i ) - { - os_ << " "; - } - } - - void space() - { - if( pretty_ ) os_ << ' '; - } - - void new_line() - { - if( pretty_ ) os_ << '\n'; - } - - Generator& operator=( const Generator& ); // to prevent "assignment operator could not be generated" warning - - Ostream_type& os_; - int indentation_level_; - bool pretty_; - }; - - template< class Value_type, class Ostream_type > - void write_stream( const Value_type& value, Ostream_type& os, bool pretty ) - { - Generator< Value_type, Ostream_type >( value, os, pretty ); - } - - template< class Value_type > - typename Value_type::String_type write_string( const Value_type& value, bool pretty ) - { - typedef typename Value_type::String_type::value_type Char_type; - - std::basic_ostringstream< Char_type > os; - - write_stream( value, os, pretty ); - - return os.str(); - } -} - -#endif diff --git a/src/main.cpp b/src/main.cpp index 39da3406a8..e92f64413a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,7 @@ #include "chainparams.h" #include "checkpoints.h" #include "checkqueue.h" +#include "consensus/validation.h" #include "init.h" #include "merkleblock.h" #include "net.h" @@ -28,6 +29,7 @@ #include <boost/algorithm/string/replace.hpp> #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> +#include <boost/math/distributions/poisson.hpp> #include <boost/thread.hpp> using namespace std; @@ -57,7 +59,7 @@ bool fPruneMode = false; bool fIsBareMultisigStd = true; bool fCheckBlockIndex = false; bool fCheckpointsEnabled = true; -unsigned int nCoinCacheSize = 5000; +size_t nCoinCacheUsage = 5000 * 300; uint64_t nPruneTarget = 0; /** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */ @@ -75,10 +77,9 @@ void EraseOrphansFor(NodeId peer); /** * Returns true if there are nRequired or more blocks of minVersion or above - * in the last Params().ToCheckBlockUpgradeMajority() blocks, starting at pstart - * and going backwards. + * in the last Consensus::Params::nMajorityWindow blocks, starting at pstart and going backwards. */ -static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired); +static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned nRequired, const Consensus::Params& consensusParams); static void CheckBlockIndex(); /** Constant stuff for coinbase transactions we create: */ @@ -154,8 +155,8 @@ namespace { uint256 hash; CBlockIndex *pindex; //! Optional. int64_t nTime; //! Time of "getdata" request in microseconds. - int nValidatedQueuedBefore; //! Number of blocks queued with validated headers (globally) at the time this one is requested. bool fValidatedHeaders; //! Whether this block has validated headers at the time of request. + int64_t nTimeDisconnect; //! The timeout for this block request (for disconnecting a slow peer) }; map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight; @@ -216,6 +217,7 @@ struct CNodeState { int64_t nStallingSince; list<QueuedBlock> vBlocksInFlight; int nBlocksInFlight; + int nBlocksInFlightValidHeaders; //! Whether we consider this a preferred download peer. bool fPreferredDownload; @@ -229,6 +231,7 @@ struct CNodeState { fSyncStarted = false; nStallingSince = 0; nBlocksInFlight = 0; + nBlocksInFlightValidHeaders = 0; fPreferredDownload = false; } }; @@ -260,6 +263,12 @@ void UpdatePreferredDownload(CNode* node, CNodeState* state) nPreferredDownload += state->fPreferredDownload; } +// Returns time at which to timeout block request (nTime in microseconds) +int64_t GetBlockTimeout(int64_t nTime, int nValidatedQueuedBefore, const Consensus::Params &consensusParams) +{ + return nTime + 500000 * consensusParams.nPowTargetSpacing * (4 + nValidatedQueuedBefore); +} + void InitializeNode(NodeId nodeid, const CNode *pnode) { LOCK(cs_main); CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; @@ -287,30 +296,36 @@ void FinalizeNode(NodeId nodeid) { } // Requires cs_main. -void MarkBlockAsReceived(const uint256& hash) { +// Returns a bool indicating whether we requested this block. +bool MarkBlockAsReceived(const uint256& hash) { map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); if (itInFlight != mapBlocksInFlight.end()) { CNodeState *state = State(itInFlight->second.first); nQueuedValidatedHeaders -= itInFlight->second.second->fValidatedHeaders; + state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders; state->vBlocksInFlight.erase(itInFlight->second.second); state->nBlocksInFlight--; state->nStallingSince = 0; mapBlocksInFlight.erase(itInFlight); + return true; } + return false; } // Requires cs_main. -void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, CBlockIndex *pindex = NULL) { +void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL) { CNodeState *state = State(nodeid); assert(state != NULL); // Make sure it's not listed somewhere already. MarkBlockAsReceived(hash); - QueuedBlock newentry = {hash, pindex, GetTimeMicros(), nQueuedValidatedHeaders, pindex != NULL}; + int64_t nNow = GetTimeMicros(); + QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders, consensusParams)}; nQueuedValidatedHeaders += newentry.fValidatedHeaders; list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); state->nBlocksInFlight++; + state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders; mapBlocksInFlight[hash] = std::make_pair(nodeid, it); } @@ -659,14 +674,8 @@ bool IsStandardTx(const CTransaction& tx, string& reason) bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) { - AssertLockHeld(cs_main); - // Time based nLockTime implemented in 0.1.6 if (tx.nLockTime == 0) return true; - if (nBlockHeight == 0) - nBlockHeight = chainActive.Height(); - if (nBlockTime == 0) - nBlockTime = GetAdjustedTime(); if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) return true; BOOST_FOREACH(const CTxIn& txin, tx.vin) @@ -675,10 +684,16 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) return true; } +bool CheckFinalTx(const CTransaction &tx) +{ + AssertLockHeld(cs_main); + return IsFinalTx(tx, chainActive.Height() + 1, GetAdjustedTime()); +} + /** * Check transaction inputs to mitigate two * potential denial-of-service attacks: - * + * * 1. scriptSigs with extra data stuffed into them, * not consumed by scriptPubKey (or P2SH script) * 2. P2SH scripts with a crazy number of expensive @@ -891,21 +906,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Only accept nLockTime-using transactions that can be mined in the next // block; we don't want our mempool filled up with transactions that can't // be mined yet. - // - // However, IsFinalTx() is confusing... Without arguments, it uses - // chainActive.Height() to evaluate nLockTime; when a block is accepted, - // chainActive.Height() is set to the value of nHeight in the block. - // However, when IsFinalTx() is called within CBlock::AcceptBlock(), the - // height of the block *being* evaluated is what is used. Thus if we want - // to know if a transaction can be part of the *next* block, we need to - // call IsFinalTx() with one more than chainActive.Height(). - // - // Timestamps on the other hand don't get any special treatment, because we - // can't know what timestamp the next block will have, and there aren't - // timestamp applications where it matters. - if (!IsFinalTx(tx, chainActive.Height() + 1)) - return state.DoS(0, - error("AcceptToMemoryPool: non-final"), + if (!CheckFinalTx(tx)) + return state.DoS(0, error("AcceptToMemoryPool: non-final"), REJECT_NONSTANDARD, "non-final"); // is it already in the memory pool? @@ -987,7 +989,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa CAmount nFees = nValueIn-nValueOut; double dPriority = view.GetPriority(tx, chainActive.Height()); - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height()); + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx)); unsigned int nSize = entry.GetTxSize(); // Don't accept it if it can't get into a block @@ -1053,7 +1055,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } // Store transaction in memory - pool.addUnchecked(hash, entry); + pool.addUnchecked(hash, entry, !IsInitialBlockDownload()); } SyncWithWallets(tx, NULL); @@ -1134,7 +1136,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock // CBlock and CBlockIndex // -bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos) +bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart) { // Open history file to append CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); @@ -1143,7 +1145,7 @@ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos) // Write index header unsigned int nSize = fileout.GetSerializeSize(block); - fileout << FLATDATA(Params().MessageStart()) << nSize; + fileout << FLATDATA(messageStart) << nSize; // Write block long fileOutPos = ftell(fileout.Get()); @@ -1189,19 +1191,17 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) return true; } -CAmount GetBlockValue(int nHeight, const CAmount& nFees) +CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) { - CAmount nSubsidy = 50 * COIN; - int halvings = nHeight / Params().SubsidyHalvingInterval(); - + int halvings = nHeight / consensusParams.nSubsidyHalvingInterval; // Force block reward to zero when right shift is undefined. if (halvings >= 64) - return nFees; + return 0; + CAmount nSubsidy = 50 * COIN; // Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years. nSubsidy >>= halvings; - - return nSubsidy + nFees; + return nSubsidy; } bool IsInitialBlockDownload() @@ -1499,7 +1499,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi namespace { -bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock) +bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart) { // Open history file to append CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); @@ -1508,7 +1508,7 @@ bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint // Write index header unsigned int nSize = fileout.GetSerializeSize(blockundo); - fileout << FLATDATA(Params().MessageStart()) << nSize; + fileout << FLATDATA(messageStart) << nSize; // Write undo data long fileOutPos = ftell(fileout.Get()); @@ -1553,6 +1553,24 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uin return true; } +/** Abort with a message */ +bool AbortNode(const std::string& strMessage, const std::string& userMessage="") +{ + strMiscWarning = strMessage; + LogPrintf("*** %s\n", strMessage); + uiInterface.ThreadSafeMessageBox( + userMessage.empty() ? _("Error: A fatal internal error occured, see debug.log for details") : userMessage, + "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return false; +} + +bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage="") +{ + AbortNode(strMessage, userMessage); + return state.Error(strMessage); +} + } // anon namespace /** @@ -1688,6 +1706,64 @@ void ThreadScriptCheck() { scriptcheckqueue.Thread(); } +// +// Called periodically asynchronously; alerts if it smells like +// we're being fed a bad chain (blocks being generated much +// too slowly or too quickly). +// +void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const CChain& chain, int64_t nPowTargetSpacing) +{ + if (initialDownloadCheck()) return; + + static int64_t lastAlertTime = 0; + int64_t now = GetAdjustedTime(); + if (lastAlertTime > now-60*60*24) return; // Alert at most once per day + + const int SPAN_HOURS=4; + const int SPAN_SECONDS=SPAN_HOURS*60*60; + int BLOCKS_EXPECTED = SPAN_SECONDS / nPowTargetSpacing; + + boost::math::poisson_distribution<double> poisson(BLOCKS_EXPECTED); + + std::string strWarning; + int64_t startTime = GetAdjustedTime()-SPAN_SECONDS; + + LOCK(cs); + int h = chain.Height(); + while (h > 0 && chain[h]->GetBlockTime() >= startTime) + --h; + int nBlocks = chain.Height()-h; + + // How likely is it to find that many by chance? + double p = boost::math::pdf(poisson, nBlocks); + + LogPrint("partitioncheck", "%s : Found %d blocks in the last %d hours\n", __func__, nBlocks, SPAN_HOURS); + LogPrint("partitioncheck", "%s : likelihood: %g\n", __func__, p); + + // Aim for one false-positive about every fifty years of normal running: + const int FIFTY_YEARS = 50*365*24*60*60; + double alertThreshold = 1.0 / (FIFTY_YEARS / SPAN_SECONDS); + + if (p <= alertThreshold && nBlocks < BLOCKS_EXPECTED) + { + // Many fewer blocks than expected: alert! + strWarning = strprintf(_("WARNING: check your network connection, %d blocks received in the last %d hours (%d expected)"), + nBlocks, SPAN_HOURS, BLOCKS_EXPECTED); + } + else if (p <= alertThreshold && nBlocks > BLOCKS_EXPECTED) + { + // Many more blocks than expected: alert! + strWarning = strprintf(_("WARNING: abnormally high number of blocks generated, %d blocks received in the last %d hours (%d expected)"), + nBlocks, SPAN_HOURS, BLOCKS_EXPECTED); + } + if (!strWarning.empty()) + { + strMiscWarning = strWarning; + CAlert::Notify(strWarning, true); + lastAlertTime = now; + } +} + static int64_t nTimeVerify = 0; static int64_t nTimeConnect = 0; static int64_t nTimeIndex = 0; @@ -1754,7 +1830,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE; // Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks, when 75% of the network has upgraded: - if (block.nVersion >= 3 && IsSuperMajority(3, pindex->pprev, Params().EnforceBlockUpgradeMajority())) { + if (block.nVersion >= 3 && IsSuperMajority(3, pindex->pprev, chainparams.GetConsensus().nMajorityEnforceBlockUpgrade, chainparams.GetConsensus())) { flags |= SCRIPT_VERIFY_DERSIG; } @@ -1817,10 +1893,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart; LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001); - if (block.vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) + CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus()); + if (block.vtx[0].GetValueOut() > blockReward) return state.DoS(100, error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)", - block.vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees)), + block.vtx[0].GetValueOut(), blockReward), REJECT_INVALID, "bad-cb-amount"); if (!control.Wait()) @@ -1838,8 +1915,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin CDiskBlockPos pos; if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) return error("ConnectBlock(): FindUndoPos failed"); - if (!UndoWriteToDisk(blockundo, pos, pindex->pprev->GetBlockHash())) - return state.Abort("Failed to write undo data"); + if (!UndoWriteToDisk(blockundo, pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart())) + return AbortNode(state, "Failed to write undo data"); // update nUndoPos in block index pindex->nUndoPos = pos.nPos; @@ -1852,7 +1929,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (fTxIndex) if (!pblocktree->WriteTxIndex(vPos)) - return state.Abort("Failed to write transaction index"); + return AbortNode(state, "Failed to write transaction index"); // add this block to the view's block chain view.SetBestBlock(pindex->GetBlockHash()); @@ -1887,11 +1964,14 @@ enum FlushStateMode { bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { LOCK2(cs_main, cs_LastBlockFile); static int64_t nLastWrite = 0; + static int64_t nLastFlush = 0; + static int64_t nLastSetChain = 0; std::set<int> setFilesToPrune; bool fFlushForPrune = false; try { if (fPruneMode && fCheckForPruning) { FindFilesToPrune(setFilesToPrune); + fCheckForPruning = false; if (!setFilesToPrune.empty()) { fFlushForPrune = true; if (!fHavePruned) { @@ -1900,16 +1980,32 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { } } } - if ((mode == FLUSH_STATE_ALWAYS) || - ((mode == FLUSH_STATE_PERIODIC || mode == FLUSH_STATE_IF_NEEDED) && pcoinsTip->GetCacheSize() > nCoinCacheSize) || - (mode == FLUSH_STATE_PERIODIC && GetTimeMicros() > nLastWrite + DATABASE_WRITE_INTERVAL * 1000000) || - fFlushForPrune) { - // Typical CCoins structures on disk are around 100 bytes in size. - // Pushing a new one to the database can cause it to be written - // twice (once in the log, and once in the tables). This is already - // an overestimation, as most will delete an existing entry or - // overwrite one. Still, use a conservative safety factor of 2. - if (!CheckDiskSpace(100 * 2 * 2 * pcoinsTip->GetCacheSize())) + int64_t nNow = GetTimeMicros(); + // Avoid writing/flushing immediately after startup. + if (nLastWrite == 0) { + nLastWrite = nNow; + } + if (nLastFlush == 0) { + nLastFlush = nNow; + } + if (nLastSetChain == 0) { + nLastSetChain = nNow; + } + size_t cacheSize = pcoinsTip->DynamicMemoryUsage(); + // The cache is large and close to the limit, but we have time now (not in the middle of a block processing). + bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize * (10.0/9) > nCoinCacheUsage; + // The cache is over the limit, we have to write now. + bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nCoinCacheUsage; + // It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash. + bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; + // It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage. + bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; + // Combine all conditions that result in a full cache flush. + bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune; + // Write blocks and block index to disk. + if (fDoFullFlush || fPeriodicWrite) { + // Depend on nMinDiskSpace to ensure we can write block index + if (!CheckDiskSpace(0)) return state.Error("out of disk space"); // First make sure all block and undo data is flushed to disk. FlushBlockFile(); @@ -1928,27 +2024,35 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { setDirtyBlockIndex.erase(it++); } if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { - return state.Abort("Files to write to block index database"); + return AbortNode(state, "Files to write to block index database"); } } - // Flush the chainstate (which may refer to block index entries). - if (!pcoinsTip->Flush()) - return state.Abort("Failed to write to coin database"); - // Finally remove any pruned files - if (fFlushForPrune) { + if (fFlushForPrune) UnlinkPrunedFiles(setFilesToPrune); - fCheckForPruning = false; - } - + nLastWrite = nNow; + } + // Flush best chain related state. This can only be done if the blocks / block index write was also done. + if (fDoFullFlush) { + // Typical CCoins structures on disk are around 128 bytes in size. + // Pushing a new one to the database can cause it to be written + // twice (once in the log, and once in the tables). This is already + // an overestimation, as most will delete an existing entry or + // overwrite one. Still, use a conservative safety factor of 2. + if (!CheckDiskSpace(128 * 2 * 2 * pcoinsTip->GetCacheSize())) + return state.Error("out of disk space"); + // Flush the chainstate (which may refer to block index entries). + if (!pcoinsTip->Flush()) + return AbortNode(state, "Failed to write to coin database"); + nLastFlush = nNow; + } + if ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000) { // Update best block in wallet (so we can detect restored wallets). - if (mode != FLUSH_STATE_IF_NEEDED) { - GetMainSignals().SetBestChain(chainActive.GetLocator()); - } - nLastWrite = GetTimeMicros(); + GetMainSignals().SetBestChain(chainActive.GetLocator()); + nLastSetChain = nNow; } } catch (const std::runtime_error& e) { - return state.Abort(std::string("System error while flushing: ") + e.what()); + return AbortNode(state, std::string("System error while flushing: ") + e.what()); } return true; } @@ -1973,10 +2077,10 @@ void static UpdateTip(CBlockIndex *pindexNew) { nTimeBestReceived = GetTime(); mempool.AddTransactionsUpdated(1); - LogPrintf("%s: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%u\n", __func__, + LogPrintf("%s: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%.1fMiB(%utx)\n", __func__, chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), (unsigned int)pcoinsTip->GetCacheSize()); + Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); cvBlockChange.notify_all(); @@ -2012,7 +2116,7 @@ bool static DisconnectTip(CValidationState &state) { // Read block from disk. CBlock block; if (!ReadBlockFromDisk(block, pindexDelete)) - return state.Abort("Failed to read block"); + return AbortNode(state, "Failed to read block"); // Apply the block atomically to the chain state. int64_t nStart = GetTimeMicros(); { @@ -2051,7 +2155,7 @@ static int64_t nTimeFlush = 0; static int64_t nTimeChainState = 0; static int64_t nTimePostConnect = 0; -/** +/** * Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock * corresponding to pindexNew, to bypass loading it again from disk. */ @@ -2063,7 +2167,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * CBlock block; if (!pblock) { if (!ReadBlockFromDisk(block, pindexNew)) - return state.Abort("Failed to read block"); + return AbortNode(state, "Failed to read block"); pblock = █ } // Apply the block atomically to the chain state. @@ -2094,7 +2198,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001); // Remove conflicting transactions from the mempool. list<CTransaction> txConflicted; - mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted); + mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload()); mempool.check(pcoinsTip); // Update chainActive & related variables. UpdateTip(pindexNew); @@ -2646,18 +2750,14 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta } // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: - if (block.nVersion < 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityRejectBlockOutdated)) - { + if (block.nVersion < 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) return state.Invalid(error("%s: rejected nVersion=1 block", __func__), REJECT_OBSOLETE, "bad-version"); - } // Reject block.nVersion=2 blocks when 95% (75% on testnet) of the network has upgraded: - if (block.nVersion < 3 && IsSuperMajority(3, pindexPrev, consensusParams.nMajorityRejectBlockOutdated)) - { + if (block.nVersion < 3 && IsSuperMajority(3, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) return state.Invalid(error("%s : rejected nVersion=2 block", __func__), REJECT_OBSOLETE, "bad-version"); - } return true; } @@ -2665,6 +2765,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex * const pindexPrev) { const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1; + const Consensus::Params& consensusParams = Params().GetConsensus(); // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -2674,7 +2775,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): - if (block.nVersion >= 2 && IsSuperMajority(2, pindexPrev, Params().EnforceBlockUpgradeMajority())) + if (block.nVersion >= 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams)) { CScript expect = CScript() << nHeight; if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || @@ -2730,8 +2831,9 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc return true; } -bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, CDiskBlockPos* dbp) +bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp) { + const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); CBlockIndex *&pindex = *ppindex; @@ -2739,13 +2841,18 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (!AcceptBlockHeader(block, state, &pindex)) return false; - // If we're pruning, ensure that we don't allow a peer to dump a copy - // of old blocks. But we might need blocks that are not on the main chain - // to handle a reorg, even if we've processed once. - if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) { - // TODO: deal better with duplicate blocks. - // return state.DoS(20, error("AcceptBlock(): already have block %d %s", pindex->nHeight, pindex->GetBlockHash().ToString()), REJECT_DUPLICATE, "duplicate"); - return true; + // Try to process all requested blocks that we don't have, but only + // process an unrequested block if it's new and has enough work to + // advance our tip. + bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA; + bool fHasMoreWork = (chainActive.Tip() ? pindex->nChainWork > chainActive.Tip()->nChainWork : true); + + // TODO: deal better with return value and error conditions for duplicate + // and unrequested blocks. + if (fAlreadyHave) return true; + if (!fRequested) { // If we didn't ask for it: + if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned + if (!fHasMoreWork) return true; // Don't process less-work chains } if ((!CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) { @@ -2767,12 +2874,12 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != NULL)) return error("AcceptBlock(): FindBlockPos failed"); if (dbp == NULL) - if (!WriteBlockToDisk(block, blockPos)) - return state.Abort("Failed to write block"); + if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) + AbortNode(state, "Failed to write block"); if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) return error("AcceptBlock(): ReceivedBlockTransactions failed"); } catch (const std::runtime_error& e) { - return state.Abort(std::string("System error: ") + e.what()); + return AbortNode(state, std::string("System error: ") + e.what()); } if (fCheckForPruning) @@ -2781,11 +2888,10 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, return true; } -static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired) +static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned nRequired, const Consensus::Params& consensusParams) { - unsigned int nToCheck = Params().ToCheckBlockUpgradeMajority(); unsigned int nFound = 0; - for (unsigned int i = 0; i < nToCheck && nFound < nRequired && pstart != NULL; i++) + for (int i = 0; i < consensusParams.nMajorityWindow && nFound < nRequired && pstart != NULL; i++) { if (pstart->nVersion >= minVersion) ++nFound; @@ -2795,21 +2901,22 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned } -bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) +bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp) { // Preliminary checks bool checked = CheckBlock(*pblock, state); { LOCK(cs_main); - MarkBlockAsReceived(pblock->GetHash()); + bool fRequested = MarkBlockAsReceived(pblock->GetHash()); + fRequested |= fForceProcessing; if (!checked) { return error("%s: CheckBlock FAILED", __func__); } // Store to disk CBlockIndex *pindex = NULL; - bool ret = AcceptBlock(*pblock, state, &pindex, dbp); + bool ret = AcceptBlock(*pblock, state, &pindex, fRequested, dbp); if (pindex && pfrom) { mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId(); } @@ -2848,24 +2955,6 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex return true; } - - - - - - - -bool AbortNode(const std::string &strMessage, const std::string &userMessage) { - strMiscWarning = strMessage; - LogPrintf("*** %s\n", strMessage); - uiInterface.ThreadSafeMessageBox( - userMessage.empty() ? _("Error: A fatal internal error occured, see debug.log for details") : userMessage, - "", CClientUIInterface::MSG_ERROR); - StartShutdown(); - return false; -} - - /** * BLOCK PRUNING CODE */ @@ -2934,7 +3023,7 @@ void FindFilesToPrune(std::set<int>& setFilesToPrune) return; } - unsigned int nLastBlockWeMustKeep = chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP; + unsigned int nLastBlockWeCanPrune = chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP; uint64_t nCurrentUsage = CalculateCurrentUsage(); // We don't check to prune until after we've allocated new space for files // So we should leave a buffer under our target to account for another allocation @@ -2954,7 +3043,7 @@ void FindFilesToPrune(std::set<int>& setFilesToPrune) break; // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip - if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeMustKeep) + if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) break; PruneOneBlockFile(fileNumber); @@ -2965,10 +3054,10 @@ void FindFilesToPrune(std::set<int>& setFilesToPrune) } } - LogPrint("prune", "Prune: target=%dMiB actual=%dMiB diff=%dMiB min_must_keep=%d removed %d blk/rev pairs\n", + LogPrint("prune", "Prune: target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n", nPruneTarget/1024/1024, nCurrentUsage/1024/1024, ((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024, - nLastBlockWeMustKeep, count); + nLastBlockWeCanPrune, count); } bool CheckDiskSpace(uint64_t nAdditionalBytes) @@ -3199,7 +3288,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth } } // check level 3: check for inconsistencies during memory-only disconnect of tip blocks - if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= nCoinCacheSize) { + if (nCheckLevel >= 3 && pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) { bool fClean = true; if (!DisconnectBlock(block, state, pindex, coins, &fClean)) return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); @@ -3276,6 +3365,7 @@ bool LoadBlockIndex() bool InitBlockIndex() { + const CChainParams& chainparams = Params(); LOCK(cs_main); // Check whether we're already initialized if (chainActive.Genesis() != NULL) @@ -3296,7 +3386,7 @@ bool InitBlockIndex() { CValidationState state; if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.GetBlockTime())) return error("LoadBlockIndex(): FindBlockPos failed"); - if (!WriteBlockToDisk(block, blockPos)) + if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) return error("LoadBlockIndex(): writing genesis block to disk failed"); CBlockIndex *pindex = AddToBlockIndex(block); if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) @@ -3374,7 +3464,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) // process in case the block isn't known yet if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) { CValidationState state; - if (ProcessNewBlock(state, NULL, &block, dbp)) + if (ProcessNewBlock(state, NULL, &block, true, dbp)) nLoaded++; if (state.IsError()) break; @@ -3396,7 +3486,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), head.ToString()); CValidationState dummy; - if (ProcessNewBlock(dummy, NULL, &block, &it->second)) + if (ProcessNewBlock(dummy, NULL, &block, true, &it->second)) { nLoaded++; queue.push_back(block.GetHash()); @@ -3608,7 +3698,7 @@ void static CheckBlockIndex() // CAlert // -string GetWarnings(string strFor) +std::string GetWarnings(const std::string& strFor) { int nPriority = 0; string strStatusBar; @@ -4091,7 +4181,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vToFetch.push_back(inv); // Mark block as in flight already, even though the actual "getdata" message only goes out // later (within the same cs_main lock, though). - MarkBlockAsInFlight(pfrom->GetId(), inv.hash); + MarkBlockAsInFlight(pfrom->GetId(), inv.hash, chainparams.GetConsensus()); } LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id); } @@ -4176,6 +4266,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LOCK(cs_main); + if (IsInitialBlockDownload()) + return true; + CBlockIndex* pindex = NULL; if (locator.IsNull()) { @@ -4380,7 +4473,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->AddInventoryKnown(inv); CValidationState state; - ProcessNewBlock(state, pfrom, &block); + // Process all blocks from whitelisted peers, even if not requested. + ProcessNewBlock(state, pfrom, &block, pfrom->fWhitelisted, NULL); int nDoS; if (state.IsInvalid(nDoS)) { pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), @@ -4937,9 +5031,22 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // timeout. We compensate for in-flight blocks to prevent killing off peers due to our own downstream link // being saturated. We only count validated in-flight blocks so peers can't advertise non-existing block hashes // to unreasonably increase our timeout. - if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0 && state.vBlocksInFlight.front().nTime < nNow - 500000 * consensusParams.nPowTargetSpacing * (4 + state.vBlocksInFlight.front().nValidatedQueuedBefore)) { - LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", state.vBlocksInFlight.front().hash.ToString(), pto->id); - pto->fDisconnect = true; + // We also compare the block download timeout originally calculated against the time at which we'd disconnect + // if we assumed the block were being requested now (ignoring blocks we've requested from this peer, since we're + // only looking at this peer's oldest request). This way a large queue in the past doesn't result in a + // permanently large window for this block to be delivered (ie if the number of blocks in flight is decreasing + // more quickly than once every 5 minutes, then we'll shorten the download window for this block). + if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0) { + QueuedBlock &queuedBlock = state.vBlocksInFlight.front(); + int64_t nTimeoutIfRequestedNow = GetBlockTimeout(nNow, nQueuedValidatedHeaders - state.nBlocksInFlightValidHeaders, consensusParams); + if (queuedBlock.nTimeDisconnect > nTimeoutIfRequestedNow) { + LogPrint("net", "Reducing block download timeout for peer=%d block=%s, orig=%d new=%d\n", pto->id, queuedBlock.hash.ToString(), queuedBlock.nTimeDisconnect, nTimeoutIfRequestedNow); + queuedBlock.nTimeDisconnect = nTimeoutIfRequestedNow; + } + if (queuedBlock.nTimeDisconnect < nNow) { + LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->id); + pto->fDisconnect = true; + } } // @@ -4952,7 +5059,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller); BOOST_FOREACH(CBlockIndex *pindex, vToDownload) { vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); - MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), pindex); + MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex); LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), pindex->nHeight, pto->id); } diff --git a/src/main.h b/src/main.h index 90b809e85e..abaedae207 100644 --- a/src/main.h +++ b/src/main.h @@ -82,21 +82,13 @@ static const unsigned int MAX_HEADERS_RESULTS = 2000; * degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning * harder). We'll probably want to make this a per-peer adaptive value at some point. */ static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024; -/** Time to wait (in seconds) between writing blockchain state to disk. */ -static const unsigned int DATABASE_WRITE_INTERVAL = 3600; +/** Time to wait (in seconds) between writing blocks/block index to disk. */ +static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60; +/** Time to wait (in seconds) between flushing chainstate to disk. */ +static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60; /** Maximum length of reject messages. */ static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111; -/** "reject" message codes */ -static const unsigned char REJECT_MALFORMED = 0x01; -static const unsigned char REJECT_INVALID = 0x10; -static const unsigned char REJECT_OBSOLETE = 0x11; -static const unsigned char REJECT_DUPLICATE = 0x12; -static const unsigned char REJECT_NONSTANDARD = 0x40; -static const unsigned char REJECT_DUST = 0x41; -static const unsigned char REJECT_INSUFFICIENTFEE = 0x42; -static const unsigned char REJECT_CHECKPOINT = 0x43; - struct BlockHasher { size_t operator()(const uint256& hash) const { return hash.GetCheapHash(); } @@ -119,7 +111,7 @@ extern bool fTxIndex; extern bool fIsBareMultisigStd; extern bool fCheckBlockIndex; extern bool fCheckpointsEnabled; -extern unsigned int nCoinCacheSize; +extern size_t nCoinCacheUsage; extern CFeeRate minRelayTxFee; /** Best header we've seen so far (used for getheaders queries' starting points). */ @@ -161,10 +153,11 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals); * @param[out] state This may be set to an Error state if any error occurred processing it, including during validation/connection/etc of otherwise unrelated blocks during reorganisation; or it may be set to an Invalid state if pblock is itself invalid (but this is not guaranteed even when the block is checked). If you want to *possibly* get feedback on whether pblock is valid, you must also install a CValidationInterface (see validationinterface.h) - this will have its BlockChecked method called whenever *any* block completes validation. * @param[in] pfrom The node which we are receiving the block from; it is added to mapBlockSource and may be penalised if the block is invalid. * @param[in] pblock The block we want to process. + * @param[in] fForceProcessing Process this block even if unrequested; used for non-network block sources and whitelisted peers. * @param[out] dbp If pblock is stored to disk (or already there), this will be set to its location. * @return True if state.IsValid() */ -bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL); +bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp); /** Check whether enough disk space is available for an incoming block */ bool CheckDiskSpace(uint64_t nAdditionalBytes = 0); /** Open a block file (blk?????.dat) */ @@ -192,15 +185,17 @@ bool ProcessMessages(CNode* pfrom); bool SendMessages(CNode* pto, bool fSendTrickle); /** Run an instance of the script checking thread */ void ThreadScriptCheck(); +/** Try to detect Partition (network isolation) attacks against us */ +void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const CChain& chain, int64_t nPowTargetSpacing); /** Check whether we are doing an initial block download (synchronizing from disk or network) */ bool IsInitialBlockDownload(); /** Format a string that describes several potential problems detected by the core */ -std::string GetWarnings(std::string strFor); +std::string GetWarnings(const std::string& strFor); /** Retrieve a transaction (from memory pool, or from disk, if possible) */ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false); /** Find the best known block, and make it the tip of the block chain */ bool ActivateBestChain(CValidationState &state, CBlock *pblock = NULL); -CAmount GetBlockValue(int nHeight, const CAmount& nFees); +CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); /** * Prune block and undo files (blk???.dat and undo???.dat) so that the disk space used is less than a user-defined target. @@ -226,8 +221,6 @@ void UnlinkPrunedFiles(std::set<int>& setFilesToPrune); /** Create a new block index entry for a given block hash */ CBlockIndex * InsertBlockIndex(uint256 hash); -/** Abort with a message */ -bool AbortNode(const std::string &msg, const std::string &userMessage=""); /** Get statistics from node state */ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats); /** Increase a node's misbehavior score. */ @@ -332,7 +325,18 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state); */ bool IsStandardTx(const CTransaction& tx, std::string& reason); -bool IsFinalTx(const CTransaction &tx, int nBlockHeight = 0, int64_t nBlockTime = 0); +/** + * Check if transaction is final and can be included in a block with the + * specified height and time. Consensus critical. + */ +bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime); + +/** + * Check if transaction will be final in the next block to be created. + * + * Calls IsFinalTx() with current block height and appropriate block time. + */ +bool CheckFinalTx(const CTransaction &tx); /** * Closure representing one script verification @@ -370,7 +374,7 @@ public: /** Functions for disk access for blocks */ -bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos); +bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart); bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos); bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex); @@ -397,8 +401,8 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn /** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex *pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true); -/** Store block on disk. If dbp is provided, the file is known to already reside on disk */ -bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, CDiskBlockPos* dbp = NULL); +/** Store block on disk. If dbp is non-NULL, the file is known to already reside on disk */ +bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, bool fRequested, CDiskBlockPos* dbp); bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex **ppindex= NULL); @@ -457,69 +461,6 @@ public: } }; -/** Capture information about block/transaction validation */ -class CValidationState { -private: - enum mode_state { - MODE_VALID, //! everything ok - MODE_INVALID, //! network rule violation (DoS value may be set) - MODE_ERROR, //! run-time error - } mode; - int nDoS; - std::string strRejectReason; - unsigned char chRejectCode; - bool corruptionPossible; -public: - CValidationState() : mode(MODE_VALID), nDoS(0), chRejectCode(0), corruptionPossible(false) {} - bool DoS(int level, bool ret = false, - unsigned char chRejectCodeIn=0, std::string strRejectReasonIn="", - bool corruptionIn=false) { - chRejectCode = chRejectCodeIn; - strRejectReason = strRejectReasonIn; - corruptionPossible = corruptionIn; - if (mode == MODE_ERROR) - return ret; - nDoS += level; - mode = MODE_INVALID; - return ret; - } - bool Invalid(bool ret = false, - unsigned char _chRejectCode=0, std::string _strRejectReason="") { - return DoS(0, ret, _chRejectCode, _strRejectReason); - } - bool Error(std::string strRejectReasonIn="") { - if (mode == MODE_VALID) - strRejectReason = strRejectReasonIn; - mode = MODE_ERROR; - return false; - } - bool Abort(const std::string &msg) { - AbortNode(msg); - return Error(msg); - } - bool IsValid() const { - return mode == MODE_VALID; - } - bool IsInvalid() const { - return mode == MODE_INVALID; - } - bool IsError() const { - return mode == MODE_ERROR; - } - bool IsInvalid(int &nDoSOut) const { - if (IsInvalid()) { - nDoSOut = nDoS; - return true; - } - return false; - } - bool CorruptionPossible() const { - return corruptionPossible; - } - unsigned char GetRejectCode() const { return chRejectCode; } - std::string GetRejectReason() const { return strRejectReason; } -}; - /** RAII wrapper for VerifyDB: Verify consistency of the block and coin databases */ class CVerifyDB { public: diff --git a/src/memusage.h b/src/memusage.h new file mode 100644 index 0000000000..9f7de9e2e1 --- /dev/null +++ b/src/memusage.h @@ -0,0 +1,111 @@ +// Copyright (c) 2015 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_MEMUSAGE_H +#define BITCOIN_MEMUSAGE_H + +#include <stdlib.h> + +#include <map> +#include <set> +#include <vector> + +#include <boost/unordered_set.hpp> +#include <boost/unordered_map.hpp> + +namespace memusage +{ + +/** Compute the total memory used by allocating alloc bytes. */ +static size_t MallocUsage(size_t alloc); + +/** Compute the memory used for dynamically allocated but owned data structures. + * For generic data types, this is *not* recursive. DynamicUsage(vector<vector<int> >) + * will compute the memory used for the vector<int>'s, but not for the ints inside. + * This is for efficiency reasons, as these functions are intended to be fast. If + * application data structures require more accurate inner accounting, they should + * do the recursion themselves, or use more efficient caching + updating on modification. + */ +template<typename X> static size_t DynamicUsage(const std::vector<X>& v); +template<typename X> static size_t DynamicUsage(const std::set<X>& s); +template<typename X, typename Y> static size_t DynamicUsage(const std::map<X, Y>& m); +template<typename X, typename Y> static size_t DynamicUsage(const boost::unordered_set<X, Y>& s); +template<typename X, typename Y, typename Z> static size_t DynamicUsage(const boost::unordered_map<X, Y, Z>& s); +template<typename X> static size_t DynamicUsage(const X& x); + +static inline size_t MallocUsage(size_t alloc) +{ + // Measured on libc6 2.19 on Linux. + if (sizeof(void*) == 8) { + return ((alloc + 31) >> 4) << 4; + } else if (sizeof(void*) == 4) { + return ((alloc + 15) >> 3) << 3; + } else { + assert(0); + } +} + +// STL data structures + +template<typename X> +struct stl_tree_node +{ +private: + int color; + void* parent; + void* left; + void* right; + X x; +}; + +template<typename X> +static inline size_t DynamicUsage(const std::vector<X>& v) +{ + return MallocUsage(v.capacity() * sizeof(X)); +} + +template<typename X> +static inline size_t DynamicUsage(const std::set<X>& s) +{ + return MallocUsage(sizeof(stl_tree_node<X>)) * s.size(); +} + +template<typename X, typename Y> +static inline size_t DynamicUsage(const std::map<X, Y>& m) +{ + return MallocUsage(sizeof(stl_tree_node<std::pair<const X, Y> >)) * m.size(); +} + +// Boost data structures + +template<typename X> +struct boost_unordered_node : private X +{ +private: + void* ptr; +}; + +template<typename X, typename Y> +static inline size_t DynamicUsage(const boost::unordered_set<X, Y>& s) +{ + return MallocUsage(sizeof(boost_unordered_node<X>)) * s.size() + MallocUsage(sizeof(void*) * s.bucket_count()); +} + +template<typename X, typename Y, typename Z> +static inline size_t DynamicUsage(const boost::unordered_map<X, Y, Z>& m) +{ + return MallocUsage(sizeof(boost_unordered_node<std::pair<const X, Y> >)) * m.size() + MallocUsage(sizeof(void*) * m.bucket_count()); +} + +// Dispatch to class method as fallback + +template<typename X> +static inline size_t DynamicUsage(const X& x) +{ + return x.DynamicMemoryUsage(); +} + +} + +#endif diff --git a/src/miner.cpp b/src/miner.cpp index 56a2c5828b..f5919ca3af 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -8,6 +8,7 @@ #include "amount.h" #include "chainparams.h" #include "consensus/consensus.h" +#include "consensus/validation.h" #include "hash.h" #include "main.h" #include "net.h" @@ -91,6 +92,7 @@ void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) { + const CChainParams& chainparams = Params(); // Create new block auto_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate()); if(!pblocktemplate.get()) @@ -136,6 +138,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) LOCK2(cs_main, mempool.cs); CBlockIndex* pindexPrev = chainActive.Tip(); const int nHeight = pindexPrev->nHeight + 1; + pblock->nTime = GetAdjustedTime(); CCoinsViewCache view(pcoinsTip); // Priority order to process transactions @@ -150,7 +153,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) mi != mempool.mapTx.end(); ++mi) { const CTransaction& tx = mi->second.GetTx(); - if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight)) + if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight, pblock->nTime)) continue; COrphan* porphan = NULL; @@ -320,7 +323,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize); // Compute final coinbase transaction. - txNew.vout[0].nValue = GetBlockValue(nHeight, nFees); + txNew.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); txNew.vin[0].scriptSig = CScript() << nHeight << OP_0; pblock->vtx[0] = txNew; pblocktemplate->vTxFees[0] = -nFees; @@ -431,7 +434,7 @@ static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& rese // Process this block the same as if we had received it from another node CValidationState state; - if (!ProcessNewBlock(state, NULL, pblock)) + if (!ProcessNewBlock(state, NULL, pblock, true, NULL)) return error("BitcoinMiner: ProcessNewBlock, block not accepted"); return true; @@ -453,8 +456,16 @@ void static BitcoinMiner(CWallet *pwallet) if (chainparams.MiningRequiresPeers()) { // Busy-wait for the network to come online so we don't waste time mining // on an obsolete chain. In regtest mode we expect to fly solo. - while (vNodes.empty()) + do { + bool fvNodesEmpty; + { + LOCK(cs_vNodes); + fvNodesEmpty = vNodes.empty(); + } + if (!fvNodesEmpty && !IsInitialBlockDownload()) + break; MilliSleep(1000); + } while (true); } // @@ -533,6 +544,11 @@ void static BitcoinMiner(CWallet *pwallet) LogPrintf("BitcoinMiner terminated\n"); throw; } + catch (const std::runtime_error &e) + { + LogPrintf("BitcoinMiner runtime error: %s\n", e.what()); + return; + } } void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads) diff --git a/src/net.cpp b/src/net.cpp index 2de04fc574..42ac0e50ea 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -13,6 +13,7 @@ #include "chainparams.h" #include "clientversion.h" #include "primitives/transaction.h" +#include "scheduler.h" #include "ui_interface.h" #include "crypto/common.h" @@ -106,7 +107,7 @@ boost::condition_variable messageHandlerCondition; static CNodeSignals g_signals; CNodeSignals& GetNodeSignals() { return g_signals; } -void AddOneShot(string strDest) +void AddOneShot(const std::string& strDest) { LOCK(cs_vOneShots); vOneShots.push_back(strDest); @@ -1123,7 +1124,7 @@ void ThreadDNSAddressSeed() vector<CAddress> vAdd; if (LookupHost(seed.host.c_str(), vIPs)) { - BOOST_FOREACH(CNetAddr& ip, vIPs) + BOOST_FOREACH(const CNetAddr& ip, vIPs) { int nOneDay = 24*3600; CAddress addr = CAddress(CService(ip, Params().GetDefaultPort())); @@ -1187,7 +1188,7 @@ void ThreadOpenConnections() for (int64_t nLoop = 0;; nLoop++) { ProcessOneShot(); - BOOST_FOREACH(string strAddr, mapMultiArgs["-connect"]) + BOOST_FOREACH(const std::string& strAddr, mapMultiArgs["-connect"]) { CAddress addr; OpenNetworkConnection(addr, NULL, strAddr.c_str()); @@ -1290,10 +1291,10 @@ void ThreadOpenAddedConnections() list<string> lAddresses(0); { LOCK(cs_vAddedNodes); - BOOST_FOREACH(string& strAddNode, vAddedNodes) + BOOST_FOREACH(const std::string& strAddNode, vAddedNodes) lAddresses.push_back(strAddNode); } - BOOST_FOREACH(string& strAddNode, lAddresses) { + BOOST_FOREACH(const std::string& strAddNode, lAddresses) { CAddress addr; CSemaphoreGrant grant(*semOutbound); OpenNetworkConnection(addr, &grant, strAddNode.c_str()); @@ -1308,20 +1309,19 @@ void ThreadOpenAddedConnections() list<string> lAddresses(0); { LOCK(cs_vAddedNodes); - BOOST_FOREACH(string& strAddNode, vAddedNodes) + BOOST_FOREACH(const std::string& strAddNode, vAddedNodes) lAddresses.push_back(strAddNode); } list<vector<CService> > lservAddressesToAdd(0); - BOOST_FOREACH(string& strAddNode, lAddresses) - { + BOOST_FOREACH(const std::string& strAddNode, lAddresses) { vector<CService> vservNode(0); if(Lookup(strAddNode.c_str(), vservNode, Params().GetDefaultPort(), fNameLookup, 0)) { lservAddressesToAdd.push_back(vservNode); { LOCK(cs_setservAddNodeAddresses); - BOOST_FOREACH(CService& serv, vservNode) + BOOST_FOREACH(const CService& serv, vservNode) setservAddNodeAddresses.insert(serv); } } @@ -1332,7 +1332,7 @@ void ThreadOpenAddedConnections() LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) for (list<vector<CService> >::iterator it = lservAddressesToAdd.begin(); it != lservAddressesToAdd.end(); it++) - BOOST_FOREACH(CService& addrNode, *(it)) + BOOST_FOREACH(const CService& addrNode, *(it)) if (pnode->addr == addrNode) { it = lservAddressesToAdd.erase(it); @@ -1362,7 +1362,7 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) || FindNode(addrConnect.ToStringIPPort())) return false; - } else if (FindNode(pszDest)) + } else if (FindNode(std::string(pszDest))) return false; CNode* pnode = ConnectNode(addrConnect, pszDest); @@ -1384,7 +1384,7 @@ void ThreadMessageHandler() { boost::mutex condition_mutex; boost::unique_lock<boost::mutex> lock(condition_mutex); - + SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); while (true) { @@ -1590,7 +1590,7 @@ void static Discover(boost::thread_group& threadGroup) #endif } -void StartNode(boost::thread_group& threadGroup) +void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) { uiInterface.InitMessage(_("Loading addresses...")); // Load addresses for peers.dat @@ -1640,7 +1640,7 @@ void StartNode(boost::thread_group& threadGroup) threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "msghand", &ThreadMessageHandler)); // Dump network addresses - threadGroup.create_thread(boost::bind(&LoopForever<void (*)()>, "dumpaddr", &DumpAddresses, DUMP_ADDRESSES_INTERVAL * 1000)); + scheduler.scheduleEvery(&DumpAddresses, DUMP_ADDRESSES_INTERVAL); } bool StopNode() @@ -1905,7 +1905,7 @@ bool CAddrDB::Read(CAddrMan& addr) unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); } unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); } -CNode::CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn, bool fInboundIn) : +CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn) : ssSend(SER_NETWORK, INIT_PROTO_VERSION), addrKnown(5000, 0.001, insecure_rand()), setInventoryKnown(SendBufferSize() / 1000) @@ -32,6 +32,7 @@ class CAddrMan; class CBlockIndex; +class CScheduler; class CNode; namespace boost { @@ -62,7 +63,7 @@ static const size_t MAPASKFOR_MAX_SZ = MAX_INV_SZ; unsigned int ReceiveFloodSize(); unsigned int SendBufferSize(); -void AddOneShot(std::string strDest); +void AddOneShot(const std::string& strDest); void AddressCurrentlyConnected(const CService& addr); CNode* FindNode(const CNetAddr& ip); CNode* FindNode(const std::string& addrName); @@ -72,7 +73,7 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu void MapPort(bool fUseUPnP); unsigned short GetListenPort(); bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); -void StartNode(boost::thread_group& threadGroup); +void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler); bool StopNode(); void SocketSendData(CNode *pnode); @@ -320,7 +321,7 @@ public: // Whether a ping is requested. bool fPingQueued; - CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn = "", bool fInboundIn=false); + CNode(SOCKET hSocketIn, const CAddress &addrIn, const std::string &addrNameIn = "", bool fInboundIn = false); ~CNode(); private: diff --git a/src/netbase.cpp b/src/netbase.cpp index 2015d0271a..e3cb4e706f 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -35,8 +35,6 @@ #define MSG_NOSIGNAL 0 #endif -using namespace std; - // Settings static proxyType proxyInfo[NET_MAX]; static proxyType nameProxy; @@ -597,13 +595,13 @@ bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout, b bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed) { - string strDest; + std::string strDest; int port = portDefault; if (outProxyConnectionFailed) *outProxyConnectionFailed = false; - SplitHostPort(string(pszDest), port, strDest); + SplitHostPort(std::string(pszDest), port, strDest); proxyType nameProxy; GetNameProxy(nameProxy); @@ -1252,15 +1250,15 @@ CSubNet::CSubNet(const std::string &strSubnet, bool fAllowLookup) std::string strNetmask = strSubnet.substr(slash + 1); int32_t n; // IPv4 addresses start at offset 12, and first 12 bytes must match, so just offset n - int noffset = network.IsIPv4() ? (12 * 8) : 0; + const int astartofs = network.IsIPv4() ? 12 : 0; if (ParseInt32(strNetmask, &n)) // If valid number, assume /24 symtex { - if(n >= 0 && n <= (128 - noffset)) // Only valid if in range of bits of address + if(n >= 0 && n <= (128 - astartofs*8)) // Only valid if in range of bits of address { - n += noffset; + n += astartofs*8; // Clear bits [n..127] for (; n < 128; ++n) - netmask[n>>3] &= ~(1<<(n&7)); + netmask[n>>3] &= ~(1<<(7-(n&7))); } else { @@ -1271,12 +1269,10 @@ CSubNet::CSubNet(const std::string &strSubnet, bool fAllowLookup) { if (LookupHost(strNetmask.c_str(), vIP, 1, false)) // Never allow lookup for netmask { - // Remember: GetByte returns bytes in reversed order // Copy only the *last* four bytes in case of IPv4, the rest of the mask should stay 1's as // we don't want pchIPv4 to be part of the mask. - int asize = network.IsIPv4() ? 4 : 16; - for(int x=0; x<asize; ++x) - netmask[15-x] = vIP[0].GetByte(x); + for(int x=astartofs; x<16; ++x) + netmask[x] = vIP[0].ip[x]; } else { @@ -1289,6 +1285,10 @@ CSubNet::CSubNet(const std::string &strSubnet, bool fAllowLookup) { valid = false; } + + // Normalize network according to netmask + for(int x=0; x<16; ++x) + network.ip[x] &= netmask[x]; } bool CSubNet::Match(const CNetAddr &addr) const @@ -1296,7 +1296,7 @@ bool CSubNet::Match(const CNetAddr &addr) const if (!valid || !addr.IsValid()) return false; for(int x=0; x<16; ++x) - if ((addr.GetByte(x) & netmask[15-x]) != network.GetByte(x)) + if ((addr.ip[x] & netmask[x]) != network.ip[x]) return false; return true; } diff --git a/src/netbase.h b/src/netbase.h index 6d2ca4afb2..1f2957116e 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -100,6 +100,8 @@ class CNetAddr inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(FLATDATA(ip)); } + + friend class CSubNet; }; class CSubNet diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp new file mode 100644 index 0000000000..b1491cec01 --- /dev/null +++ b/src/policy/fees.cpp @@ -0,0 +1,529 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "policy/fees.h" + +#include "amount.h" +#include "primitives/transaction.h" +#include "streams.h" +#include "txmempool.h" +#include "util.h" + +void TxConfirmStats::Initialize(std::vector<double>& defaultBuckets, + unsigned int maxConfirms, double _decay, std::string _dataTypeString) +{ + decay = _decay; + dataTypeString = _dataTypeString; + for (unsigned int i = 0; i < defaultBuckets.size(); i++) { + buckets.push_back(defaultBuckets[i]); + bucketMap[defaultBuckets[i]] = i; + } + confAvg.resize(maxConfirms); + curBlockConf.resize(maxConfirms); + unconfTxs.resize(maxConfirms); + for (unsigned int i = 0; i < maxConfirms; i++) { + confAvg[i].resize(buckets.size()); + curBlockConf[i].resize(buckets.size()); + unconfTxs[i].resize(buckets.size()); + } + + oldUnconfTxs.resize(buckets.size()); + curBlockTxCt.resize(buckets.size()); + txCtAvg.resize(buckets.size()); + curBlockVal.resize(buckets.size()); + avg.resize(buckets.size()); +} + +// Zero out the data for the current block +void TxConfirmStats::ClearCurrent(unsigned int nBlockHeight) +{ + for (unsigned int j = 0; j < buckets.size(); j++) { + oldUnconfTxs[j] += unconfTxs[nBlockHeight%unconfTxs.size()][j]; + unconfTxs[nBlockHeight%unconfTxs.size()][j] = 0; + for (unsigned int i = 0; i < curBlockConf.size(); i++) + curBlockConf[i][j] = 0; + curBlockTxCt[j] = 0; + curBlockVal[j] = 0; + } +} + + +void TxConfirmStats::Record(int blocksToConfirm, double val) +{ + // blocksToConfirm is 1-based + if (blocksToConfirm < 1) + return; + unsigned int bucketindex = bucketMap.lower_bound(val)->second; + for (size_t i = blocksToConfirm; i <= curBlockConf.size(); i++) { + curBlockConf[i - 1][bucketindex]++; + } + curBlockTxCt[bucketindex]++; + curBlockVal[bucketindex] += val; +} + +void TxConfirmStats::UpdateMovingAverages() +{ + for (unsigned int j = 0; j < buckets.size(); j++) { + for (unsigned int i = 0; i < confAvg.size(); i++) + confAvg[i][j] = confAvg[i][j] * decay + curBlockConf[i][j]; + avg[j] = avg[j] * decay + curBlockVal[j]; + txCtAvg[j] = txCtAvg[j] * decay + curBlockTxCt[j]; + } +} + +// returns -1 on error conditions +double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal, + double successBreakPoint, bool requireGreater, + unsigned int nBlockHeight) +{ + // Counters for a bucket (or range of buckets) + double nConf = 0; // Number of tx's confirmed within the confTarget + double totalNum = 0; // Total number of tx's that were ever confirmed + int extraNum = 0; // Number of tx's still in mempool for confTarget or longer + + int maxbucketindex = buckets.size() - 1; + + // requireGreater means we are looking for the lowest fee/priority such that all higher + // values pass, so we start at maxbucketindex (highest fee) and look at succesively + // smaller buckets until we reach failure. Otherwise, we are looking for the highest + // fee/priority such that all lower values fail, and we go in the opposite direction. + unsigned int startbucket = requireGreater ? maxbucketindex : 0; + int step = requireGreater ? -1 : 1; + + // We'll combine buckets until we have enough samples. + // The near and far variables will define the range we've combined + // The best variables are the last range we saw which still had a high + // enough confirmation rate to count as success. + // The cur variables are the current range we're counting. + unsigned int curNearBucket = startbucket; + unsigned int bestNearBucket = startbucket; + unsigned int curFarBucket = startbucket; + unsigned int bestFarBucket = startbucket; + + bool foundAnswer = false; + unsigned int bins = unconfTxs.size(); + + // Start counting from highest(default) or lowest fee/pri transactions + for (int bucket = startbucket; bucket >= 0 && bucket <= maxbucketindex; bucket += step) { + curFarBucket = bucket; + nConf += confAvg[confTarget - 1][bucket]; + totalNum += txCtAvg[bucket]; + for (unsigned int confct = confTarget; confct < GetMaxConfirms(); confct++) + extraNum += unconfTxs[(nBlockHeight - confct)%bins][bucket]; + extraNum += oldUnconfTxs[bucket]; + // If we have enough transaction data points in this range of buckets, + // we can test for success + // (Only count the confirmed data points, so that each confirmation count + // will be looking at the same amount of data and same bucket breaks) + if (totalNum >= sufficientTxVal / (1 - decay)) { + double curPct = nConf / (totalNum + extraNum); + + // Check to see if we are no longer getting confirmed at the success rate + if (requireGreater && curPct < successBreakPoint) + break; + if (!requireGreater && curPct > successBreakPoint) + break; + + // Otherwise update the cumulative stats, and the bucket variables + // and reset the counters + else { + foundAnswer = true; + nConf = 0; + totalNum = 0; + extraNum = 0; + bestNearBucket = curNearBucket; + bestFarBucket = curFarBucket; + curNearBucket = bucket + step; + } + } + } + + double median = -1; + double txSum = 0; + + // Calculate the "average" fee of the best bucket range that met success conditions + // Find the bucket with the median transaction and then report the average fee from that bucket + // This is a compromise between finding the median which we can't since we don't save all tx's + // and reporting the average which is less accurate + unsigned int minBucket = bestNearBucket < bestFarBucket ? bestNearBucket : bestFarBucket; + unsigned int maxBucket = bestNearBucket > bestFarBucket ? bestNearBucket : bestFarBucket; + for (unsigned int j = minBucket; j <= maxBucket; j++) { + txSum += txCtAvg[j]; + } + if (foundAnswer && txSum != 0) { + txSum = txSum / 2; + for (unsigned int j = minBucket; j <= maxBucket; j++) { + if (txCtAvg[j] < txSum) + txSum -= txCtAvg[j]; + else { // we're in the right bucket + median = avg[j] / txCtAvg[j]; + break; + } + } + } + + LogPrint("estimatefee", "%3d: For conf success %s %4.2f need %s %s: %12.5g from buckets %8g - %8g Cur Bucket stats %6.2f%% %8.1f/(%.1f+%d mempool)\n", + confTarget, requireGreater ? ">" : "<", successBreakPoint, dataTypeString, + requireGreater ? ">" : "<", median, buckets[minBucket], buckets[maxBucket], + 100 * nConf / (totalNum + extraNum), nConf, totalNum, extraNum); + + return median; +} + +void TxConfirmStats::Write(CAutoFile& fileout) +{ + fileout << decay; + fileout << buckets; + fileout << avg; + fileout << txCtAvg; + fileout << confAvg; +} + +void TxConfirmStats::Read(CAutoFile& filein) +{ + // Read data file into temporary variables and do some very basic sanity checking + std::vector<double> fileBuckets; + std::vector<double> fileAvg; + std::vector<std::vector<double> > fileConfAvg; + std::vector<double> fileTxCtAvg; + double fileDecay; + size_t maxConfirms; + size_t numBuckets; + + filein >> fileDecay; + if (fileDecay <= 0 || fileDecay >= 1) + throw std::runtime_error("Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)"); + filein >> fileBuckets; + numBuckets = fileBuckets.size(); + if (numBuckets <= 1 || numBuckets > 1000) + throw std::runtime_error("Corrupt estimates file. Must have between 2 and 1000 fee/pri buckets"); + filein >> fileAvg; + if (fileAvg.size() != numBuckets) + throw std::runtime_error("Corrupt estimates file. Mismatch in fee/pri average bucket count"); + filein >> fileTxCtAvg; + if (fileTxCtAvg.size() != numBuckets) + throw std::runtime_error("Corrupt estimates file. Mismatch in tx count bucket count"); + filein >> fileConfAvg; + maxConfirms = fileConfAvg.size(); + if (maxConfirms <= 0 || maxConfirms > 6 * 24 * 7) // one week + throw std::runtime_error("Corrupt estimates file. Must maintain estimates for between 1 and 1008 (one week) confirms"); + for (unsigned int i = 0; i < maxConfirms; i++) { + if (fileConfAvg[i].size() != numBuckets) + throw std::runtime_error("Corrupt estimates file. Mismatch in fee/pri conf average bucket count"); + } + // Now that we've processed the entire fee estimate data file and not + // thrown any errors, we can copy it to our data structures + decay = fileDecay; + buckets = fileBuckets; + avg = fileAvg; + confAvg = fileConfAvg; + txCtAvg = fileTxCtAvg; + bucketMap.clear(); + + // Resize the current block variables which aren't stored in the data file + // to match the number of confirms and buckets + curBlockConf.resize(maxConfirms); + for (unsigned int i = 0; i < maxConfirms; i++) { + curBlockConf[i].resize(buckets.size()); + } + curBlockTxCt.resize(buckets.size()); + curBlockVal.resize(buckets.size()); + + unconfTxs.resize(maxConfirms); + for (unsigned int i = 0; i < maxConfirms; i++) { + unconfTxs[i].resize(buckets.size()); + } + oldUnconfTxs.resize(buckets.size()); + + for (unsigned int i = 0; i < buckets.size(); i++) + bucketMap[buckets[i]] = i; + + LogPrint("estimatefee", "Reading estimates: %u %s buckets counting confirms up to %u blocks\n", + numBuckets, dataTypeString, maxConfirms); +} + +unsigned int TxConfirmStats::NewTx(unsigned int nBlockHeight, double val) +{ + unsigned int bucketindex = bucketMap.lower_bound(val)->second; + unsigned int blockIndex = nBlockHeight % unconfTxs.size(); + unconfTxs[blockIndex][bucketindex]++; + LogPrint("estimatefee", "adding to %s\n", dataTypeString); + return bucketindex; +} + +void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight, unsigned int bucketindex) +{ + //nBestSeenHeight is not updated yet for the new block + int blocksAgo = nBestSeenHeight - entryHeight; + if (nBestSeenHeight == 0) // the BlockPolicyEstimator hasn't seen any blocks yet + blocksAgo = 0; + if (blocksAgo < 0) { + LogPrint("estimatefee", "Blockpolicy error, blocks ago is negative for mempool tx\n"); + return; //This can't happen becasue we call this with our best seen height, no entries can have higher + } + + if (blocksAgo >= (int)unconfTxs.size()) { + if (oldUnconfTxs[bucketindex] > 0) + oldUnconfTxs[bucketindex]--; + else + LogPrint("estimatefee", "Blockpolicy error, mempool tx removed from >25 blocks,bucketIndex=%u already\n", + bucketindex); + } + else { + unsigned int blockIndex = entryHeight % unconfTxs.size(); + if (unconfTxs[blockIndex][bucketindex] > 0) + unconfTxs[blockIndex][bucketindex]--; + else + LogPrint("estimatefee", "Blockpolicy error, mempool tx removed from blockIndex=%u,bucketIndex=%u already\n", + blockIndex, bucketindex); + } +} + +void CBlockPolicyEstimator::removeTx(uint256 hash) +{ + std::map<uint256, TxStatsInfo>::iterator pos = mapMemPoolTxs.find(hash); + if (pos == mapMemPoolTxs.end()) { + LogPrint("estimatefee", "Blockpolicy error mempool tx %s not found for removeTx\n", + hash.ToString().c_str()); + return; + } + TxConfirmStats *stats = pos->second.stats; + unsigned int entryHeight = pos->second.blockHeight; + unsigned int bucketIndex = pos->second.bucketIndex; + + if (stats != NULL) + stats->removeTx(entryHeight, nBestSeenHeight, bucketIndex); + mapMemPoolTxs.erase(hash); +} + +CBlockPolicyEstimator::CBlockPolicyEstimator(const CFeeRate& _minRelayFee) + : nBestSeenHeight(0) +{ + minTrackedFee = _minRelayFee < CFeeRate(MIN_FEERATE) ? CFeeRate(MIN_FEERATE) : _minRelayFee; + std::vector<double> vfeelist; + for (double bucketBoundary = minTrackedFee.GetFeePerK(); bucketBoundary <= MAX_FEERATE; bucketBoundary *= FEE_SPACING) { + vfeelist.push_back(bucketBoundary); + } + vfeelist.push_back(INF_FEERATE); + feeStats.Initialize(vfeelist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY, "FeeRate"); + + minTrackedPriority = AllowFreeThreshold() < MIN_PRIORITY ? MIN_PRIORITY : AllowFreeThreshold(); + std::vector<double> vprilist; + for (double bucketBoundary = minTrackedPriority; bucketBoundary <= MAX_PRIORITY; bucketBoundary *= PRI_SPACING) { + vprilist.push_back(bucketBoundary); + } + vprilist.push_back(INF_PRIORITY); + priStats.Initialize(vprilist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY, "Priority"); + + feeUnlikely = CFeeRate(0); + feeLikely = CFeeRate(INF_FEERATE); + priUnlikely = 0; + priLikely = INF_PRIORITY; +} + +bool CBlockPolicyEstimator::isFeeDataPoint(const CFeeRate &fee, double pri) +{ + if ((pri < minTrackedPriority && fee >= minTrackedFee) || + (pri < priUnlikely && fee > feeLikely)) { + return true; + } + return false; +} + +bool CBlockPolicyEstimator::isPriDataPoint(const CFeeRate &fee, double pri) +{ + if ((fee < minTrackedFee && pri >= minTrackedPriority) || + (fee < feeUnlikely && pri > priLikely)) { + return true; + } + return false; +} + +void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool fCurrentEstimate) +{ + unsigned int txHeight = entry.GetHeight(); + uint256 hash = entry.GetTx().GetHash(); + if (mapMemPoolTxs[hash].stats != NULL) { + LogPrint("estimatefee", "Blockpolicy error mempool tx %s already being tracked\n", + hash.ToString().c_str()); + return; + } + + if (txHeight < nBestSeenHeight) { + // Ignore side chains and re-orgs; assuming they are random they don't + // affect the estimate. We'll potentially double count transactions in 1-block reorgs. + return; + } + + // Only want to be updating estimates when our blockchain is synced, + // otherwise we'll miscalculate how many blocks its taking to get included. + if (!fCurrentEstimate) + return; + + if (!entry.WasClearAtEntry()) { + // This transaction depends on other transactions in the mempool to + // be included in a block before it will be able to be included, so + // we shouldn't include it in our calculations + return; + } + + // Fees are stored and reported as BTC-per-kb: + CFeeRate feeRate(entry.GetFee(), entry.GetTxSize()); + + // Want the priority of the tx at confirmation. However we don't know + // what that will be and its too hard to continue updating it + // so use starting priority as a proxy + double curPri = entry.GetPriority(txHeight); + mapMemPoolTxs[hash].blockHeight = txHeight; + + LogPrint("estimatefee", "Blockpolicy mempool tx %s ", hash.ToString().substr(0,10)); + // Record this as a priority estimate + if (entry.GetFee() == 0 || isPriDataPoint(feeRate, curPri)) { + mapMemPoolTxs[hash].stats = &priStats; + mapMemPoolTxs[hash].bucketIndex = priStats.NewTx(txHeight, curPri); + } + // Record this as a fee estimate + else if (isFeeDataPoint(feeRate, curPri)) { + mapMemPoolTxs[hash].stats = &feeStats; + mapMemPoolTxs[hash].bucketIndex = feeStats.NewTx(txHeight, (double)feeRate.GetFeePerK()); + } + else { + LogPrint("estimatefee", "not adding\n"); + } +} + +void CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry& entry) +{ + if (!entry.WasClearAtEntry()) { + // This transaction depended on other transactions in the mempool to + // be included in a block before it was able to be included, so + // we shouldn't include it in our calculations + return; + } + + // How many blocks did it take for miners to include this transaction? + // blocksToConfirm is 1-based, so a transaction included in the earliest + // possible block has confirmation count of 1 + int blocksToConfirm = nBlockHeight - entry.GetHeight(); + if (blocksToConfirm <= 0) { + // This can't happen because we don't process transactions from a block with a height + // lower than our greatest seen height + LogPrint("estimatefee", "Blockpolicy error Transaction had negative blocksToConfirm\n"); + return; + } + + // Fees are stored and reported as BTC-per-kb: + CFeeRate feeRate(entry.GetFee(), entry.GetTxSize()); + + // Want the priority of the tx at confirmation. The priority when it + // entered the mempool could easily be very small and change quickly + double curPri = entry.GetPriority(nBlockHeight); + + // Record this as a priority estimate + if (entry.GetFee() == 0 || isPriDataPoint(feeRate, curPri)) { + priStats.Record(blocksToConfirm, curPri); + } + // Record this as a fee estimate + else if (isFeeDataPoint(feeRate, curPri)) { + feeStats.Record(blocksToConfirm, (double)feeRate.GetFeePerK()); + } +} + +void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight, + std::vector<CTxMemPoolEntry>& entries, bool fCurrentEstimate) +{ + if (nBlockHeight <= nBestSeenHeight) { + // Ignore side chains and re-orgs; assuming they are random + // they don't affect the estimate. + // And if an attacker can re-org the chain at will, then + // you've got much bigger problems than "attacker can influence + // transaction fees." + return; + } + nBestSeenHeight = nBlockHeight; + + // Only want to be updating estimates when our blockchain is synced, + // otherwise we'll miscalculate how many blocks its taking to get included. + if (!fCurrentEstimate) + return; + + // Update the dynamic cutoffs + // a fee/priority is "likely" the reason your tx was included in a block if >85% of such tx's + // were confirmed in 2 blocks and is "unlikely" if <50% were confirmed in 10 blocks + LogPrint("estimatefee", "Blockpolicy recalculating dynamic cutoffs:\n"); + priLikely = priStats.EstimateMedianVal(2, SUFFICIENT_PRITXS, MIN_SUCCESS_PCT, true, nBlockHeight); + if (priLikely == -1) + priLikely = INF_PRIORITY; + + double feeLikelyEst = feeStats.EstimateMedianVal(2, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBlockHeight); + if (feeLikelyEst == -1) + feeLikely = CFeeRate(INF_FEERATE); + else + feeLikely = CFeeRate(feeLikelyEst); + + priUnlikely = priStats.EstimateMedianVal(10, SUFFICIENT_PRITXS, UNLIKELY_PCT, false, nBlockHeight); + if (priUnlikely == -1) + priUnlikely = 0; + + double feeUnlikelyEst = feeStats.EstimateMedianVal(10, SUFFICIENT_FEETXS, UNLIKELY_PCT, false, nBlockHeight); + if (feeUnlikelyEst == -1) + feeUnlikely = CFeeRate(0); + else + feeUnlikely = CFeeRate(feeUnlikelyEst); + + // Clear the current block states + feeStats.ClearCurrent(nBlockHeight); + priStats.ClearCurrent(nBlockHeight); + + // Repopulate the current block states + for (unsigned int i = 0; i < entries.size(); i++) + processBlockTx(nBlockHeight, entries[i]); + + // Update all exponential averages with the current block states + feeStats.UpdateMovingAverages(); + priStats.UpdateMovingAverages(); + + LogPrint("estimatefee", "Blockpolicy after updating estimates for %u confirmed entries, new mempool map size %u\n", + entries.size(), mapMemPoolTxs.size()); +} + +CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget) +{ + // Return failure if trying to analyze a target we're not tracking + if (confTarget <= 0 || (unsigned int)confTarget > feeStats.GetMaxConfirms()) + return CFeeRate(0); + + double median = feeStats.EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight); + + if (median < 0) + return CFeeRate(0); + + return CFeeRate(median); +} + +double CBlockPolicyEstimator::estimatePriority(int confTarget) +{ + // Return failure if trying to analyze a target we're not tracking + if (confTarget <= 0 || (unsigned int)confTarget > priStats.GetMaxConfirms()) + return -1; + + return priStats.EstimateMedianVal(confTarget, SUFFICIENT_PRITXS, MIN_SUCCESS_PCT, true, nBestSeenHeight); +} + +void CBlockPolicyEstimator::Write(CAutoFile& fileout) +{ + fileout << nBestSeenHeight; + feeStats.Write(fileout); + priStats.Write(fileout); +} + +void CBlockPolicyEstimator::Read(CAutoFile& filein) +{ + int nFileBestSeenHeight; + filein >> nFileBestSeenHeight; + feeStats.Read(filein); + priStats.Read(filein); + nBestSeenHeight = nFileBestSeenHeight; +} diff --git a/src/policy/fees.h b/src/policy/fees.h new file mode 100644 index 0000000000..ce4d782566 --- /dev/null +++ b/src/policy/fees.h @@ -0,0 +1,276 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_POLICYESTIMATOR_H +#define BITCOIN_POLICYESTIMATOR_H + +#include "amount.h" +#include "uint256.h" + +#include <map> +#include <string> +#include <vector> + +class CAutoFile; +class CFeeRate; +class CTxMemPoolEntry; + +/** \class CBlockPolicyEstimator + * The BlockPolicyEstimator is used for estimating the fee or priority needed + * for a transaction to be included in a block within a certain number of + * blocks. + * + * At a high level the algorithm works by grouping transactions into buckets + * based on having similar priorities or fees and then tracking how long it + * takes transactions in the various buckets to be mined. It operates under + * the assumption that in general transactions of higher fee/priority will be + * included in blocks before transactions of lower fee/priority. So for + * example if you wanted to know what fee you should put on a transaction to + * be included in a block within the next 5 blocks, you would start by looking + * at the bucket with with the highest fee transactions and verifying that a + * sufficiently high percentage of them were confirmed within 5 blocks and + * then you would look at the next highest fee bucket, and so on, stopping at + * the last bucket to pass the test. The average fee of transactions in this + * bucket will give you an indication of the lowest fee you can put on a + * transaction and still have a sufficiently high chance of being confirmed + * within your desired 5 blocks. + * + * When a transaction enters the mempool or is included within a block we + * decide whether it can be used as a data point for fee estimation, priority + * estimation or neither. If the value of exactly one of those properties was + * below the required minimum it can be used to estimate the other. In + * addition, if a priori our estimation code would indicate that the + * transaction would be much more quickly included in a block because of one + * of the properties compared to the other, we can also decide to use it as + * an estimate for that property. + * + * Here is a brief description of the implementation for fee estimation. + * When a transaction that counts for fee estimation enters the mempool, we + * track the height of the block chain at entry. Whenever a block comes in, + * we count the number of transactions in each bucket and the total amount of fee + * paid in each bucket. Then we calculate how many blocks Y it took each + * transaction to be mined and we track an array of counters in each bucket + * for how long it to took transactions to get confirmed from 1 to a max of 25 + * and we increment all the counters from Y up to 25. This is because for any + * number Z>=Y the transaction was successfully mined within Z blocks. We + * want to save a history of this information, so at any time we have a + * counter of the total number of transactions that happened in a given fee + * bucket and the total number that were confirmed in each number 1-25 blocks + * or less for any bucket. We save this history by keeping an exponentially + * decaying moving average of each one of these stats. Furthermore we also + * keep track of the number unmined (in mempool) transactions in each bucket + * and for how many blocks they have been outstanding and use that to increase + * the number of transactions we've seen in that fee bucket when calculating + * an estimate for any number of confirmations below the number of blocks + * they've been outstanding. + */ + +/** + * We will instantiate two instances of this class, one to track transactions + * that were included in a block due to fee, and one for tx's included due to + * priority. We will lump transactions into a bucket according to their approximate + * fee or priority and then track how long it took for those txs to be included in a block + * + * The tracking of unconfirmed (mempool) transactions is completely independent of the + * historical tracking of transactions that have been confirmed in a block. + */ +class TxConfirmStats +{ +private: + //Define the buckets we will group transactions into (both fee buckets and priority buckets) + std::vector<double> buckets; // The upper-bound of the range for the bucket (inclusive) + std::map<double, unsigned int> bucketMap; // Map of bucket upper-bound to index into all vectors by bucket + + // For each bucket X: + // Count the total # of txs in each bucket + // Track the historical moving average of this total over blocks + std::vector<double> txCtAvg; + // and calcuate the total for the current block to update the moving average + std::vector<int> curBlockTxCt; + + // Count the total # of txs confirmed within Y blocks in each bucket + // Track the historical moving average of theses totals over blocks + std::vector<std::vector<double> > confAvg; // confAvg[Y][X] + // and calcuate the totals for the current block to update the moving averages + std::vector<std::vector<int> > curBlockConf; // curBlockConf[Y][X] + + // Sum the total priority/fee of all tx's in each bucket + // Track the historical moving average of this total over blocks + std::vector<double> avg; + // and calculate the total for the current block to update the moving average + std::vector<double> curBlockVal; + + // Combine the conf counts with tx counts to calculate the confirmation % for each Y,X + // Combine the total value with the tx counts to calculate the avg fee/priority per bucket + + std::string dataTypeString; + double decay; + + // Mempool counts of outstanding transactions + // For each bucket X, track the number of transactions in the mempool + // that are unconfirmed for each possible confirmation value Y + std::vector<std::vector<int> > unconfTxs; //unconfTxs[Y][X] + // transactions still unconfirmed after MAX_CONFIRMS for each bucket + std::vector<int> oldUnconfTxs; + +public: + /** + * Initialize the data structures. This is called by BlockPolicyEstimator's + * constructor with default values. + * @param defaultBuckets contains the upper limits for the bucket boundries + * @param maxConfirms max number of confirms to track + * @param decay how much to decay the historical moving average per block + * @param dataTypeString for logging purposes + */ + void Initialize(std::vector<double>& defaultBuckets, unsigned int maxConfirms, double decay, std::string dataTypeString); + + /** Clear the state of the curBlock variables to start counting for the new block */ + void ClearCurrent(unsigned int nBlockHeight); + + /** + * Record a new transaction data point in the current block stats + * @param blocksToConfirm the number of blocks it took this transaction to confirm + * @param val either the fee or the priority when entered of the transaction + * @warning blocksToConfirm is 1-based and has to be >= 1 + */ + void Record(int blocksToConfirm, double val); + + /** Record a new transaction entering the mempool*/ + unsigned int NewTx(unsigned int nBlockHeight, double val); + + /** Remove a transaction from mempool tracking stats*/ + void removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight, + unsigned int bucketIndex); + + /** Update our estimates by decaying our historical moving average and updating + with the data gathered from the current block */ + void UpdateMovingAverages(); + + /** + * Calculate a fee or priority estimate. Find the lowest value bucket (or range of buckets + * to make sure we have enough data points) whose transactions still have sufficient likelihood + * of being confirmed within the target number of confirmations + * @param confTarget target number of confirmations + * @param sufficientTxVal required average number of transactions per block in a bucket range + * @param minSuccess the success probability we require + * @param requireGreater return the lowest fee/pri such that all higher values pass minSuccess OR + * return the highest fee/pri such that all lower values fail minSuccess + * @param nBlockHeight the current block height + */ + double EstimateMedianVal(int confTarget, double sufficientTxVal, + double minSuccess, bool requireGreater, unsigned int nBlockHeight); + + /** Return the max number of confirms we're tracking */ + unsigned int GetMaxConfirms() { return confAvg.size(); } + + /** Write state of estimation data to a file*/ + void Write(CAutoFile& fileout); + + /** + * Read saved state of estimation data from a file and replace all internal data structures and + * variables with this state. + */ + void Read(CAutoFile& filein); +}; + + + +/** Track confirm delays up to 25 blocks, can't estimate beyond that */ +static const unsigned int MAX_BLOCK_CONFIRMS = 25; + +/** Decay of .998 is a half-life of 346 blocks or about 2.4 days */ +static const double DEFAULT_DECAY = .998; + +/** Require greater than 85% of X fee transactions to be confirmed within Y blocks for X to be big enough */ +static const double MIN_SUCCESS_PCT = .85; +static const double UNLIKELY_PCT = .5; + +/** Require an avg of 1 tx in the combined fee bucket per block to have stat significance */ +static const double SUFFICIENT_FEETXS = 1; + +/** Require only an avg of 1 tx every 5 blocks in the combined pri bucket (way less pri txs) */ +static const double SUFFICIENT_PRITXS = .2; + +// Minimum and Maximum values for tracking fees and priorities +static const double MIN_FEERATE = 10; +static const double MAX_FEERATE = 1e7; +static const double INF_FEERATE = MAX_MONEY; +static const double MIN_PRIORITY = 10; +static const double MAX_PRIORITY = 1e16; +static const double INF_PRIORITY = 1e9 * MAX_MONEY; + +// We have to lump transactions into buckets based on fee or priority, but we want to be able +// to give accurate estimates over a large range of potential fees and priorities +// Therefore it makes sense to exponentially space the buckets +/** Spacing of FeeRate buckets */ +static const double FEE_SPACING = 1.1; + +/** Spacing of Priority buckets */ +static const double PRI_SPACING = 2; + +/** + * We want to be able to estimate fees or priorities that are needed on tx's to be included in + * a certain number of blocks. Every time a block is added to the best chain, this class records + * stats on the transactions included in that block + */ +class CBlockPolicyEstimator +{ +public: + /** Create new BlockPolicyEstimator and initialize stats tracking classes with default values */ + CBlockPolicyEstimator(const CFeeRate& minRelayFee); + + /** Process all the transactions that have been included in a block */ + void processBlock(unsigned int nBlockHeight, + std::vector<CTxMemPoolEntry>& entries, bool fCurrentEstimate); + + /** Process a transaction confirmed in a block*/ + void processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry& entry); + + /** Process a transaction accepted to the mempool*/ + void processTransaction(const CTxMemPoolEntry& entry, bool fCurrentEstimate); + + /** Remove a transaction from the mempool tracking stats*/ + void removeTx(uint256 hash); + + /** Is this transaction likely included in a block because of its fee?*/ + bool isFeeDataPoint(const CFeeRate &fee, double pri); + + /** Is this transaction likely included in a block because of its priority?*/ + bool isPriDataPoint(const CFeeRate &fee, double pri); + + /** Return a fee estimate */ + CFeeRate estimateFee(int confTarget); + + /** Return a priority estimate */ + double estimatePriority(int confTarget); + + /** Write estimation data to a file */ + void Write(CAutoFile& fileout); + + /** Read estimation data from a file */ + void Read(CAutoFile& filein); + +private: + CFeeRate minTrackedFee; //! Passed to constructor to avoid dependency on main + double minTrackedPriority; //! Set to AllowFreeThreshold + unsigned int nBestSeenHeight; + struct TxStatsInfo + { + TxConfirmStats *stats; + unsigned int blockHeight; + unsigned int bucketIndex; + TxStatsInfo() : stats(NULL), blockHeight(0), bucketIndex(0) {} + }; + + // map of txids to information about that transaction + std::map<uint256, TxStatsInfo> mapMemPoolTxs; + + /** Classes to track historical data on transaction confirmations */ + TxConfirmStats feeStats, priStats; + + /** Breakpoints to help determine whether a transaction was confirmed by priority or Fee */ + CFeeRate feeLikely, feeUnlikely; + double priLikely, priUnlikely; +}; +#endif /*BITCOIN_POLICYESTIMATOR_H */ diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 018169cfdc..8740b98b70 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -26,6 +26,7 @@ #include "init.h" #include "main.h" #include "rpcserver.h" +#include "scheduler.h" #include "ui_interface.h" #include "util.h" @@ -178,6 +179,7 @@ signals: private: boost::thread_group threadGroup; + CScheduler scheduler; /// Pass fatal exception message to UI thread void handleRunawayException(const std::exception *e); @@ -258,7 +260,7 @@ void BitcoinCore::initialize() try { qDebug() << __func__ << ": Running AppInit2 in thread"; - int rv = AppInit2(threadGroup); + int rv = AppInit2(threadGroup, scheduler); if(rv) { /* Start a dummy RPC thread if no RPC thread is active yet diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 63af146fd0..c899e95506 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -45,6 +45,7 @@ <file alias="about">res/icons/about.png</file> <file alias="about_qt">res/icons/about_qt.png</file> <file alias="verify">res/icons/verify.png</file> + <file alias="warning">res/icons/warning.png</file> </qresource> <qresource prefix="/movies"> <file alias="spinner-000">res/movies/spinner-000.png</file> diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 70bf894599..efba0f5e18 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -414,6 +414,7 @@ void BitcoinGUI::createToolBars() if(walletFrame) { QToolBar *toolbar = addToolBar(tr("Tabs toolbar")); + toolbar->setMovable(false); toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); toolbar->addAction(overviewAction); toolbar->addAction(sendCoinsAction); diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index d14343717a..b259d038f2 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -275,6 +275,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "RPC server options:"), QT_TRANSLATE_NOOP("bitcoin-core", "RPC support for HTTP persistent connections (default: %d)"), QT_TRANSLATE_NOOP("bitcoin-core", "Randomly drop 1 of every <n> network messages"), QT_TRANSLATE_NOOP("bitcoin-core", "Randomly fuzz 1 of every <n> network messages"), +QT_TRANSLATE_NOOP("bitcoin-core", "Rebuild block chain index from current blk000??.dat files on startup"), QT_TRANSLATE_NOOP("bitcoin-core", "Relay and mine data carrier transactions (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Relay non-P2SH multisig (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Rescan the block chain for missing wallet transactions"), diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index 53d416ef38..6d792d1475 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -59,21 +59,35 @@ </widget> </item> <item> - <widget class="QLabel" name="labelWalletStatus"> - <property name="cursor"> - <cursorShape>WhatsThisCursor</cursorShape> + <widget class="QPushButton" name="labelWalletStatus"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="maximumSize"> + <size> + <width>30</width> + <height>16777215</height> + </size> </property> <property name="toolTip"> <string>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</string> </property> - <property name="styleSheet"> - <string notr="true">QLabel { color: red; }</string> - </property> <property name="text"> - <string notr="true">(out of sync)</string> + <string/> </property> - <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + <property name="icon"> + <iconset resource="../bitcoin.qrc"> + <normaloff>:/icons/warning</normaloff> + <disabledoff>:/icons/warning</disabledoff>:/icons/warning</iconset> + </property> + <property name="iconSize"> + <size> + <width>24</width> + <height>24</height> + </size> + </property> + <property name="flat"> + <bool>true</bool> </property> </widget> </item> @@ -431,21 +445,35 @@ </widget> </item> <item> - <widget class="QLabel" name="labelTransactionsStatus"> - <property name="cursor"> - <cursorShape>WhatsThisCursor</cursorShape> + <widget class="QPushButton" name="labelTransactionsStatus"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="maximumSize"> + <size> + <width>30</width> + <height>16777215</height> + </size> </property> <property name="toolTip"> <string>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</string> </property> - <property name="styleSheet"> - <string notr="true">QLabel { color: red; }</string> - </property> <property name="text"> - <string notr="true">(out of sync)</string> + <string/> </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + <property name="icon"> + <iconset resource="../bitcoin.qrc"> + <normaloff>:/icons/warning</normaloff> + <disabledoff>:/icons/warning</disabledoff>:/icons/warning</iconset> + </property> + <property name="iconSize"> + <size> + <width>24</width> + <height>24</height> + </size> + </property> + <property name="flat"> + <bool>true</bool> </property> </widget> </item> diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 48d0dd093c..df06f36833 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -21,15 +21,21 @@ <string>This is a normal payment.</string> </property> <property name="frameShape"> - <enum>QFrame::StyledPanel</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Sunken</enum> + <enum>QFrame::NoFrame</enum> </property> <layout class="QGridLayout" name="gridLayout"> - <property name="spacing"> + <property name="topMargin"> + <number>8</number> + </property> + <property name="bottomMargin"> + <number>4</number> + </property> + <property name="horizontalSpacing"> <number>12</number> </property> + <property name="verticalSpacing"> + <number>8</number> + </property> <item row="0" column="0"> <widget class="QLabel" name="payToLabel"> <property name="text"> @@ -193,6 +199,13 @@ </property> </widget> </item> + <item row="4" column="0" colspan="2"> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> </layout> </widget> <widget class="QFrame" name="SendCoins_UnauthenticatedPaymentRequest"> @@ -618,10 +631,7 @@ <bool>true</bool> </property> <property name="frameShape"> - <enum>QFrame::StyledPanel</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Sunken</enum> + <enum>QFrame::NoFrame</enum> </property> <layout class="QGridLayout" name="gridLayout_is"> <property name="spacing"> @@ -1150,10 +1160,7 @@ <bool>true</bool> </property> <property name="frameShape"> - <enum>QFrame::StyledPanel</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Sunken</enum> + <enum>QFrame::NoFrame</enum> </property> <layout class="QGridLayout" name="gridLayout_s"> <property name="spacing"> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 4a1f728e18..581d2321bc 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -265,6 +265,19 @@ void copyEntryData(QAbstractItemView *view, int column, int role) } } +QString getEntryData(QAbstractItemView *view, int column, int role) +{ + if(!view || !view->selectionModel()) + return QString(); + QModelIndexList selection = view->selectionModel()->selectedRows(column); + + if(!selection.isEmpty()) { + // Return first item + return (selection.at(0).data(role).toString()); + } + return QString(); +} + QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedSuffixOut) diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index bcbb540c37..55df64a256 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -64,6 +64,14 @@ namespace GUIUtil */ void copyEntryData(QAbstractItemView *view, int column, int role=Qt::EditRole); + /** Return a field of the currently selected entry as a QString. Does nothing if nothing + is selected. + @param[in] column Data column to extract from the model + @param[in] role Data role to extract from the model + @see TransactionView::copyLabel, TransactionView::copyAmount, TransactionView::copyAddress + */ + QString getEntryData(QAbstractItemView *view, int column, int role); + void setClipboard(const QString& str); /** Get save filename, mimics QFileDialog::getSaveFileName, except that it appends a default suffix @@ -205,7 +213,7 @@ namespace GUIUtil #else typedef QProgressBar ProgressBar; #endif - + } // namespace GUIUtil #endif // BITCOIN_QT_GUIUTIL_H diff --git a/src/qt/locale/bitcoin_ar.ts b/src/qt/locale/bitcoin_ar.ts index a2e4c376b8..235b22cd1f 100644 --- a/src/qt/locale/bitcoin_ar.ts +++ b/src/qt/locale/bitcoin_ar.ts @@ -765,11 +765,7 @@ <source>Your current total balance</source> <translation>رصيدك الكلي الحالي</translation> </message> - <message> - <source>out of sync</source> - <translation>خارج المزامنه</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> </context> diff --git a/src/qt/locale/bitcoin_be_BY.ts b/src/qt/locale/bitcoin_be_BY.ts index a94f3c6f7d..b727b75566 100644 --- a/src/qt/locale/bitcoin_be_BY.ts +++ b/src/qt/locale/bitcoin_be_BY.ts @@ -2,6 +2,10 @@ <context> <name>AddressBookPage</name> <message> + <source>Right-click to edit address or label</source> + <translation>Правы клік, каб рэдагаваць адрас ці метку</translation> + </message> + <message> <source>Create a new address</source> <translation>Стварыць новы адрас</translation> </message> @@ -62,10 +66,26 @@ <translation>адрасы Прымання</translation> </message> <message> + <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source> + <translation>Тут знаходзяцца Біткойн-адрасы для высылання плацяжоў. Заўсёды спраўджвайце колькасць і адрас прызначэння перад здзяйсненнем транзакцыі.</translation> + </message> + <message> + <source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source> + <translation>Тут знаходзяцца Біткойн-адрасы для прымання плацяжоў. Пажадана выкарыстоўваць новы адрас для кожнай транзакцыі.</translation> + </message> + <message> + <source>Copy &Label</source> + <translation>Капіяваць Метку</translation> + </message> + <message> <source>&Edit</source> <translation>Рэдагаваць</translation> </message> <message> + <source>Export Address List</source> + <translation>Экспартаваць Спіс Адрасоў</translation> + </message> + <message> <source>Comma separated file (*.csv)</source> <translation>Коскамі падзелены файл (*.csv)</translation> </message> @@ -73,12 +93,16 @@ <source>Exporting Failed</source> <translation>Экспартаванне няўдалае</translation> </message> - </context> + <message> + <source>There was an error trying to save the address list to %1. Please try again.</source> + <translation>Адбылася памылка падчас спробы захаваць адрас у %1. Паспрабуйце зноў.</translation> + </message> +</context> <context> <name>AddressTableModel</name> <message> <source>Label</source> - <translation>Пазнака</translation> + <translation>Метка</translation> </message> <message> <source>Address</source> @@ -92,6 +116,10 @@ <context> <name>AskPassphraseDialog</name> <message> + <source>Passphrase Dialog</source> + <translation>Дыялог сакрэтнай фразы</translation> + </message> + <message> <source>Enter passphrase</source> <translation>Увядзіце кодавую фразу</translation> </message> @@ -140,6 +168,14 @@ <translation>Ці ўпэўненыя вы, што жадаеце зашыфраваць свой гаманец?</translation> </message> <message> + <source>Bitcoin Core will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> + <translation>Bitcoin Core зараз будзе зачынены, каб фіналізаваць працэс шыфравання. Памятайце, што шыфраванне вашага гаманца не гарантуе абсалютную абарону ад магчымасці крадзяжу біткойнаў шкоднымі праграмамі, якія могуць інфікаваць ваш камп'ютар.</translation> + </message> + <message> + <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> + <translation>ВАЖНА: Усе папярэднія копіі гаманца варта замяніць новым зашыфраваным файлам. У мэтах бяспекі папярэднія копіі незашыфраванага файла-гаманца стануць неўжывальнымі, калі вы станеце карыстацца новым зашыфраваным гаманцом.</translation> + </message> + <message> <source>Warning: The Caps Lock key is on!</source> <translation>Увага: Caps Lock уключаны!</translation> </message> @@ -148,6 +184,14 @@ <translation>Гаманец зашыфраваны</translation> </message> <message> + <source>Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>.</source> + <translation>Увядзіце новы пароль для гаманца.<br/>Парольная фраза павинна складацца<b> не меньш чым з дзесяці сімвалаў</b>, ці <b>больш чым з васьмі слоў</b>.</translation> + </message> + <message> + <source>Enter the old passphrase and new passphrase to the wallet.</source> + <translation>Увядзіце стары пароль і новы пароль для гаманца.</translation> + </message> + <message> <source>Wallet encryption failed</source> <translation>Шыфраванне гаманца няўдалае</translation> </message> @@ -231,10 +275,42 @@ <translation>Зашыфраваць Гаманец...</translation> </message> <message> + <source>&Backup Wallet...</source> + <translation>Стварыць копію гаманца...</translation> + </message> + <message> + <source>&Change Passphrase...</source> + <translation>&Change Passphrase...</translation> + </message> + <message> + <source>&Sending addresses...</source> + <translation>Адрасы дасылання...</translation> + </message> + <message> + <source>&Receiving addresses...</source> + <translation>Адрасы прымання...</translation> + </message> + <message> <source>Open &URI...</source> <translation>Адчыниць &URI...</translation> </message> <message> + <source>Bitcoin Core client</source> + <translation>Bitcoin Core кліент</translation> + </message> + <message> + <source>Importing blocks from disk...</source> + <translation>Імпартуюцца блокі з дыску...</translation> + </message> + <message> + <source>Reindexing blocks on disk...</source> + <translation>Пераіндэксацыя блокаў на дыску...</translation> + </message> + <message> + <source>Send coins to a Bitcoin address</source> + <translation>Даслаць манеты на Біткойн-адрас</translation> + </message> + <message> <source>Backup wallet to another location</source> <translation>Зрабіце копію гаманца ў іншае месца</translation> </message> @@ -247,6 +323,18 @@ <translation>Вакно адладкі</translation> </message> <message> + <source>Open debugging and diagnostic console</source> + <translation>Адкрыць кансоль дыягностыкі і адладкі</translation> + </message> + <message> + <source>&Verify message...</source> + <translation>Праверыць паведамленне...</translation> + </message> + <message> + <source>Bitcoin</source> + <translation>Біткойн</translation> + </message> + <message> <source>Wallet</source> <translation>Гаманец</translation> </message> @@ -259,6 +347,10 @@ <translation>Атрымаць</translation> </message> <message> + <source>Show information about Bitcoin Core</source> + <translation>Паказаць інфармацыю аб Bitcoin Core</translation> + </message> + <message> <source>&Show / Hide</source> <translation>&Паказаць / Схаваць</translation> </message> @@ -271,6 +363,14 @@ <translation>Зашыфраваць прыватныя ключы, якия належаць вашаму гаманцу</translation> </message> <message> + <source>Sign messages with your Bitcoin addresses to prove you own them</source> + <translation>Падпісаць паведамленне з дапамогай Біткойн-адраса каб даказаць, што яно належыць вам</translation> + </message> + <message> + <source>Verify messages to ensure they were signed with specified Bitcoin addresses</source> + <translation>Спраўдзіць паведамленне з дапамогай Біткойн-адраса каб даказаць, што яно належыць вам</translation> + </message> + <message> <source>&File</source> <translation>Ф&айл</translation> </message> @@ -283,10 +383,98 @@ <translation>Дапамога</translation> </message> <message> + <source>Bitcoin Core</source> + <translation>Bitcoin Core</translation> + </message> + <message> + <source>Request payments (generates QR codes and bitcoin: URIs)</source> + <translation>Запатрабаваць плацёж (генеруецца QR-код для біткойн URI)</translation> + </message> + <message> + <source>&About Bitcoin Core</source> + <translation>Аб Bitcoin Core</translation> + </message> + <message> + <source>Modify configuration options for Bitcoin Core</source> + <translation>Мадыфікаваць опцыі канфігурацыі Bitcoin Core</translation> + </message> + <message> + <source>Show the list of used sending addresses and labels</source> + <translation>Паказаць спіс адрасоў і метак для дасылання</translation> + </message> + <message> + <source>Show the list of used receiving addresses and labels</source> + <translation>Паказаць спіс адрасоў і метак для прымання</translation> + </message> + <message> + <source>Open a bitcoin: URI or payment request</source> + <translation>Адкрыць біткойн: URI ці запыт плацяжу</translation> + </message> + <message> + <source>&Command-line options</source> + <translation>Опцыі каманднага радка</translation> + </message> + <message> + <source>Show the Bitcoin Core help message to get a list with possible Bitcoin command-line options</source> + <translation>Паказваць даведку Bitcoin Core каб атрымаць спіс магчымых опцый каманднага радка</translation> + </message> + <message numerus="yes"> + <source>%n active connection(s) to Bitcoin network</source> + <translation><numerusform>%n актыўнае злучэнне з сецівам Bitcoin</numerusform><numerusform>%n актыўных злучэнняў з сецівам Bitcoin</numerusform><numerusform>%n актыўных злучэнняў з сецівам Bitcoin</numerusform><numerusform>%n актыўных злучэнняў з сецівам Bitcoin</numerusform></translation> + </message> + <message> + <source>No block source available...</source> + <translation>Крыніца блокаў недасяжная...</translation> + </message> + <message numerus="yes"> + <source>Processed %n block(s) of transaction history.</source> + <translation><numerusform>Апрацаваны %n блок гісторыі транзакцый.</numerusform><numerusform>Апрацавана %n блокі гісторыі транзакцый.</numerusform><numerusform>Апрацавана %n блокаў гісторыі транзакцый.</numerusform><numerusform>Апрацавана %n блокаў гісторыі транзакцый.</numerusform></translation> + </message> + <message numerus="yes"> + <source>%n hour(s)</source> + <translation><numerusform>%n гадзіна</numerusform><numerusform>%n гадзіны</numerusform><numerusform>%n гадзін</numerusform><numerusform>%n гадзін</numerusform></translation> + </message> + <message numerus="yes"> + <source>%n day(s)</source> + <translation><numerusform>%n дзень</numerusform><numerusform>%n дні</numerusform><numerusform>%n дзён</numerusform><numerusform>%n дзён</numerusform></translation> + </message> + <message numerus="yes"> + <source>%n week(s)</source> + <translation><numerusform>%n тыдзень</numerusform><numerusform>%n тыдні</numerusform><numerusform>%n тыдняў</numerusform><numerusform>%n тыдняў</numerusform></translation> + </message> + <message> + <source>%1 and %2</source> + <translation>%1 і %2</translation> + </message> + <message numerus="yes"> + <source>%n year(s)</source> + <translation><numerusform>%n год</numerusform><numerusform>%n гады</numerusform><numerusform>%n гадоў</numerusform><numerusform>%n гадоў</numerusform></translation> + </message> + <message> + <source>%1 behind</source> + <translation>%1 таму</translation> + </message> + <message> + <source>Last received block was generated %1 ago.</source> + <translation>Апошні прыняты блок генераваны %1 таму.</translation> + </message> + <message> + <source>Transactions after this will not yet be visible.</source> + <translation>Транзакцыи пасля гэтай не будуць бачныя.</translation> + </message> + <message> <source>Error</source> <translation>Памылка</translation> </message> <message> + <source>Warning</source> + <translation>Увага</translation> + </message> + <message> + <source>Information</source> + <translation>Інфармацыя</translation> + </message> + <message> <source>Up to date</source> <translation>Сінхранізавана</translation> </message> @@ -295,6 +483,36 @@ <translation>Наганяем...</translation> </message> <message> + <source>Date: %1 +</source> + <translation>Дата: %1 +</translation> + </message> + <message> + <source>Amount: %1 +</source> + <translation>Колькасць: %1 +</translation> + </message> + <message> + <source>Type: %1 +</source> + <translation>Тып: %1 +</translation> + </message> + <message> + <source>Label: %1 +</source> + <translation>Метка: %1 +</translation> + </message> + <message> + <source>Address: %1 +</source> + <translation>Адрас: %1 +</translation> + </message> + <message> <source>Sent transaction</source> <translation>Дасланыя транзакцыі</translation> </message> @@ -313,26 +531,82 @@ </context> <context> <name>ClientModel</name> - </context> + <message> + <source>Network Alert</source> + <translation>Трывога Сеціва</translation> + </message> +</context> <context> <name>CoinControlDialog</name> <message> + <source>Quantity:</source> + <translation>Колькасць:</translation> + </message> + <message> + <source>Bytes:</source> + <translation>Байтаў:</translation> + </message> + <message> <source>Amount:</source> <translation>Колькасць:</translation> </message> <message> + <source>Priority:</source> + <translation>Прыярытэт:</translation> + </message> + <message> + <source>Fee:</source> + <translation>Камісія:</translation> + </message> + <message> + <source>Dust:</source> + <translation>Пыл:</translation> + </message> + <message> + <source>After Fee:</source> + <translation>Пасля камісіі:</translation> + </message> + <message> + <source>(un)select all</source> + <translation>(не)выбраць ўсё</translation> + </message> + <message> + <source>Tree mode</source> + <translation>Рэжым дрэва</translation> + </message> + <message> + <source>List mode</source> + <translation>Рэжым спіса</translation> + </message> + <message> <source>Amount</source> <translation>Колькасць</translation> </message> <message> + <source>Received with label</source> + <translation>Прыняць праз метку</translation> + </message> + <message> + <source>Received with address</source> + <translation>Прыняць праз адрас</translation> + </message> + <message> <source>Date</source> <translation>Дата</translation> </message> <message> + <source>Confirmations</source> + <translation>Пацверджанняў</translation> + </message> + <message> <source>Confirmed</source> <translation>Пацверджана</translation> </message> <message> + <source>Priority</source> + <translation>Прыярытэт</translation> + </message> + <message> <source>Copy address</source> <translation>Капіяваць адрас</translation> </message> @@ -349,6 +623,90 @@ <translation>Капіяваць ID транзакцыі</translation> </message> <message> + <source>Lock unspent</source> + <translation>Замкнуць непатрачанае</translation> + </message> + <message> + <source>Unlock unspent</source> + <translation>Адамкнуць непатрачанае</translation> + </message> + <message> + <source>Copy quantity</source> + <translation>Капіяваць колькасць</translation> + </message> + <message> + <source>Copy fee</source> + <translation>Капіяваць камісію</translation> + </message> + <message> + <source>Copy after fee</source> + <translation>Капіяваць з выняткам камісіі</translation> + </message> + <message> + <source>Copy bytes</source> + <translation>Капіяваць байты</translation> + </message> + <message> + <source>Copy priority</source> + <translation>Капіяваць прыярытэт</translation> + </message> + <message> + <source>Copy dust</source> + <translation>Капіяваць пыл</translation> + </message> + <message> + <source>highest</source> + <translation>найвышэйшы</translation> + </message> + <message> + <source>higher</source> + <translation>вышэйшы</translation> + </message> + <message> + <source>high</source> + <translation>высокі</translation> + </message> + <message> + <source>medium-high</source> + <translation>вышэй сярэдняга</translation> + </message> + <message> + <source>medium</source> + <translation>сярэдні</translation> + </message> + <message> + <source>low-medium</source> + <translation>ніжэй сярэдняга</translation> + </message> + <message> + <source>low</source> + <translation>нізкі</translation> + </message> + <message> + <source>lower</source> + <translation>ніжэйшы</translation> + </message> + <message> + <source>lowest</source> + <translation>найніжэйшы</translation> + </message> + <message> + <source>yes</source> + <translation>так</translation> + </message> + <message> + <source>no</source> + <translation>не</translation> + </message> + <message> + <source>This means a fee of at least %1 per kB is required.</source> + <translation>Гэта значыць патрэбную камісію мінімум %1 на Кб.</translation> + </message> + <message> + <source>Transactions with higher priority are more likely to get included into a block.</source> + <translation>Транзакцыя большага прыярытэту больш прываблівая для ўключэння ў блок.</translation> + </message> + <message> <source>(no label)</source> <translation>непазначаны</translation> </message> @@ -361,7 +719,7 @@ </message> <message> <source>&Label</source> - <translation>Пазнака</translation> + <translation>Метка</translation> </message> <message> <source>&Address</source> @@ -398,10 +756,26 @@ </context> <context> <name>FreespaceChecker</name> + <message> + <source>A new data directory will be created.</source> + <translation>Будзе створаны новы каталог з данымі.</translation> + </message> + <message> + <source>name</source> + <translation>імя</translation> + </message> + <message> + <source>Directory already exists. Add %1 if you intend to create a new directory here.</source> + <translation>Каталог ужо існуе. Дадайце %1 калі вы збіраецеся стварыць тут новы каталог.</translation> + </message> </context> <context> <name>HelpMessageDialog</name> <message> + <source>Bitcoin Core</source> + <translation>Bitcoin Core</translation> + </message> + <message> <source>(%1-bit)</source> <translation>(%1-біт)</translation> </message> @@ -410,19 +784,51 @@ <translation>Аб Bitcoin Core</translation> </message> <message> + <source>Command-line options</source> + <translation>Опцыі каманднага радка</translation> + </message> + <message> <source>Usage:</source> <translation>Ужыванне:</translation> </message> - </context> + <message> + <source>command-line options</source> + <translation>опцыі каманднага радка</translation> + </message> +</context> <context> <name>Intro</name> <message> + <source>Welcome</source> + <translation>Вітаем</translation> + </message> + <message> + <source>Welcome to Bitcoin Core.</source> + <translation>Вітаем у Bitcoin Core.</translation> + </message> + <message> + <source>Bitcoin Core</source> + <translation>Bitcoin Core</translation> + </message> + <message> <source>Error</source> <translation>Памылка</translation> </message> - </context> + <message numerus="yes"> + <source>%n GB of free space available</source> + <translation><numerusform>%n Гб вольнага месца даступна</numerusform><numerusform>%n Гб вольнага месца даступна</numerusform><numerusform>%n Гб вольнага месца даступна</numerusform><numerusform>%n Гб вольнага месца даступна</numerusform></translation> + </message> + <message numerus="yes"> + <source>(of %n GB needed)</source> + <translation><numerusform>(з %n Гб патрэбна)</numerusform><numerusform>(з %n Гб патрэбна)</numerusform><numerusform>(з %n Гб патрэбна)</numerusform><numerusform>(з %n Гб патрэбна)</numerusform></translation> + </message> +</context> <context> <name>OpenURIDialog</name> + <message> + <source>Open URI</source> + <translation>Адкрыць URI</translation> + </message> </context> <context> <name>OptionsDialog</name> @@ -430,6 +836,10 @@ <source>Options</source> <translation>Опцыі</translation> </message> + <message> + <source>MB</source> + <translation>Мб</translation> + </message> </context> <context> <name>OverviewPage</name> @@ -461,7 +871,7 @@ <name>ReceiveCoinsDialog</name> <message> <source>&Label:</source> - <translation>Пазнака:</translation> + <translation>Метка:</translation> </message> <message> <source>Copy label</source> @@ -484,7 +894,11 @@ </message> <message> <source>Label</source> - <translation>Пазнака</translation> + <translation>Метка</translation> + </message> + <message> + <source>Message</source> + <translation>Паведамленне</translation> </message> </context> <context> @@ -495,7 +909,11 @@ </message> <message> <source>Label</source> - <translation>Пазнака</translation> + <translation>Метка</translation> + </message> + <message> + <source>Message</source> + <translation>Паведамленне</translation> </message> <message> <source>Amount</source> @@ -513,14 +931,38 @@ <translation>Даслаць Манеты</translation> </message> <message> + <source>Quantity:</source> + <translation>Колькасць:</translation> + </message> + <message> + <source>Bytes:</source> + <translation>Байтаў:</translation> + </message> + <message> <source>Amount:</source> <translation>Колькасць:</translation> </message> <message> + <source>Priority:</source> + <translation>Прыярытэт:</translation> + </message> + <message> + <source>Fee:</source> + <translation>Камісія:</translation> + </message> + <message> + <source>After Fee:</source> + <translation>Пасля камісіі:</translation> + </message> + <message> <source>Send to multiple recipients at once</source> <translation>Даслаць адразу некалькім атрымальнікам</translation> </message> <message> + <source>Dust:</source> + <translation>Пыл:</translation> + </message> + <message> <source>Balance:</source> <translation>Баланс:</translation> </message> @@ -533,10 +975,30 @@ <translation>Пацвердзіць дасыланне манет</translation> </message> <message> + <source>Copy quantity</source> + <translation>Капіяваць колькасць</translation> + </message> + <message> <source>Copy amount</source> <translation>Капіяваць колькасць</translation> </message> <message> + <source>Copy fee</source> + <translation>Капіяваць камісію</translation> + </message> + <message> + <source>Copy after fee</source> + <translation>Капіяваць з выняткам камісіі</translation> + </message> + <message> + <source>Copy bytes</source> + <translation>Капіяваць байты</translation> + </message> + <message> + <source>Copy priority</source> + <translation>Капіяваць прыярытэт</translation> + </message> + <message> <source>The amount to pay must be larger than 0.</source> <translation>Велічыня плацяжу мае быць больш за 0.</translation> </message> @@ -544,6 +1006,10 @@ <source>(no label)</source> <translation>непазначаны</translation> </message> + <message> + <source>Copy dust</source> + <translation>Капіяваць пыл</translation> + </message> </context> <context> <name>SendCoinsEntry</name> @@ -561,7 +1027,7 @@ </message> <message> <source>&Label:</source> - <translation>Пазнака:</translation> + <translation>Метка:</translation> </message> <message> <source>Alt+A</source> @@ -575,7 +1041,11 @@ <source>Alt+P</source> <translation>Alt+P</translation> </message> - </context> + <message> + <source>Memo:</source> + <translation>Памятка:</translation> + </message> +</context> <context> <name>ShutdownWindow</name> </context> @@ -597,6 +1067,10 @@ <context> <name>SplashScreen</name> <message> + <source>Bitcoin Core</source> + <translation>Bitcoin Core</translation> + </message> + <message> <source>The Bitcoin Core developers</source> <translation>Распрацоўнікі Bitcoin Core</translation> </message> @@ -607,10 +1081,18 @@ </context> <context> <name>TrafficGraphWidget</name> - </context> + <message> + <source>KB/s</source> + <translation>Кб/с</translation> + </message> +</context> <context> <name>TransactionDesc</name> <message> + <source>%1/offline</source> + <translation>%1/offline</translation> + </message> + <message> <source>%1/unconfirmed</source> <translation>%1/непацверджана</translation> </message> @@ -619,10 +1101,22 @@ <translation>%1 пацверджанняў</translation> </message> <message> + <source>Status</source> + <translation>Статус</translation> + </message> + <message> <source>Date</source> <translation>Дата</translation> </message> <message> + <source>Message</source> + <translation>Паведамленне</translation> + </message> + <message> + <source>Comment</source> + <translation>Каментар</translation> + </message> + <message> <source>Transaction ID</source> <translation>ID</translation> </message> @@ -674,7 +1168,7 @@ </message> <message> <source>Label</source> - <translation>Пазнака</translation> + <translation>Метка</translation> </message> <message> <source>Received with</source> @@ -817,7 +1311,7 @@ </message> <message> <source>Label</source> - <translation>Пазнака</translation> + <translation>Метка</translation> </message> <message> <source>Address</source> @@ -883,14 +1377,106 @@ <translation>Ужываць тэставае сеціва</translation> </message> <message> + <source>Do you want to rebuild the block database now?</source> + <translation>Ці жадаеце вы перабудаваць зараз базу звестак блокаў?</translation> + </message> + <message> + <source>Error initializing block database</source> + <translation>Памылка ініцыялізацыі базвы звестак блокаў</translation> + </message> + <message> + <source>Error initializing wallet database environment %s!</source> + <translation>Памалка ініцыялізацыі асяроддзя базы звестак гаманца %s!</translation> + </message> + <message> + <source>Error loading block database</source> + <translation>Памылка загрузкі базвы звестак блокаў</translation> + </message> + <message> + <source>Error opening block database</source> + <translation>Памылка адчынення базы звестак блокаў</translation> + </message> + <message> + <source>Error: A fatal internal error occured, see debug.log for details</source> + <translation>Памылка: здарылася Фатальная унутраная памылка, глядзі debug.log для падрабязнасцяў</translation> + </message> + <message> + <source>Error: Disk space is low!</source> + <translation>Памылка: Замала вольнага месца на дыску!</translation> + </message> + <message> + <source>Importing...</source> + <translation>Імпартаванне...</translation> + </message> + <message> + <source>Not enough file descriptors available.</source> + <translation>Не хапае файлавых дэскрыптараў.</translation> + </message> + <message> + <source>Use UPnP to map the listening port (default: %u)</source> + <translation>Use UPnP to map the listening port (default: %u)</translation> + </message> + <message> + <source>Verifying blocks...</source> + <translation>Праверка блокаў...</translation> + </message> + <message> + <source>Verifying wallet...</source> + <translation>Праверка гаманца...</translation> + </message> + <message> + <source>Wallet options:</source> + <translation>Опцыі гаманца:</translation> + </message> + <message> + <source>Imports blocks from external blk000??.dat file</source> + <translation>Імпартаванне блокаў з вонкавага blk000??.dat файла</translation> + </message> + <message> + <source>Activating best chain...</source> + <translation>Актывацыя лепшага ланцуга...</translation> + </message> + <message> + <source>Information</source> + <translation>Інфармацыя</translation> + </message> + <message> + <source>RPC server options:</source> + <translation>Опцыі RPC сервера:</translation> + </message> + <message> <source>Send trace/debug info to console instead of debug.log file</source> <translation>Слаць trace/debug звесткі ў кансоль замест файла debug.log</translation> </message> <message> + <source>Signing transaction failed</source> + <translation>Памылка подпісу транзакцыі</translation> + </message> + <message> + <source>Start minimized</source> + <translation>Стартаваць ммінімізаванай</translation> + </message> + <message> + <source>This is experimental software.</source> + <translation>Гэта эксперыментальная праграма.</translation> + </message> + <message> + <source>Transaction amount too small</source> + <translation>Транзакцыя занадта малая</translation> + </message> + <message> + <source>Transaction too large</source> + <translation>Транзакцыя занадта вялікая</translation> + </message> + <message> <source>Username for JSON-RPC connections</source> <translation>Імя карыстальника для JSON-RPC злучэнняў</translation> </message> <message> + <source>Warning</source> + <translation>Увага</translation> + </message> + <message> <source>Password for JSON-RPC connections</source> <translation>Пароль для JSON-RPC злучэнняў</translation> </message> @@ -935,6 +1521,10 @@ <translation>Загружаем гаманец...</translation> </message> <message> + <source>Cannot downgrade wallet</source> + <translation>Немагчыма рэгрэсаваць гаманец</translation> + </message> + <message> <source>Rescanning...</source> <translation>Перасканаванне...</translation> </message> diff --git a/src/qt/locale/bitcoin_bg.ts b/src/qt/locale/bitcoin_bg.ts index 5d2995802e..c86fdd42dd 100644 --- a/src/qt/locale/bitcoin_bg.ts +++ b/src/qt/locale/bitcoin_bg.ts @@ -387,6 +387,10 @@ <translation>Покажи списък с използваните адреси и имена.</translation> </message> <message> + <source>Open a bitcoin: URI or payment request</source> + <translation>Отворете биткойн: URI или заявка за плащане</translation> + </message> + <message> <source>&Command-line options</source> <translation>&Налични команди</translation> </message> @@ -481,6 +485,10 @@ <translation>Такса:</translation> </message> <message> + <source>Dust:</source> + <translation>Прах:</translation> + </message> + <message> <source>After Fee:</source> <translation>След прилагане на ДДС</translation> </message> @@ -573,6 +581,10 @@ <translation>Копиране на приоритет</translation> </message> <message> + <source>Copy dust</source> + <translation>Копирай прахта:</translation> + </message> + <message> <source>Copy change</source> <translation>Копирай рестото</translation> </message> @@ -629,6 +641,10 @@ <translation>не</translation> </message> <message> + <source>This means a fee of at least %1 per kB is required.</source> + <translation>Това означава че се изисква такса от поне %1 на килобайт.</translation> + </message> + <message> <source>Can vary +/- 1 byte per input.</source> <translation>Може да варира с +-1 байт</translation> </message> @@ -803,10 +819,22 @@ <translation>Мегабайта</translation> </message> <message> + <source>Accept connections from outside</source> + <translation>Приемай връзки отвън</translation> + </message> + <message> + <source>Allow incoming connections</source> + <translation>Позволи входящите връзки</translation> + </message> + <message> <source>IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</source> <translation>IP адрес на прокси (напр. за IPv4: 127.0.0.1 / за IPv6: ::1)</translation> </message> <message> + <source>Third party transaction URLs</source> + <translation>URL адреси на трети страни</translation> + </message> + <message> <source>Reset all client options to default.</source> <translation>Възстановете всички настройки по подразбиране.</translation> </message> @@ -891,6 +919,10 @@ <translation>Изберете единиците, показвани по подразбиране в интерфейса.</translation> </message> <message> + <source>Whether to show coin control features or not.</source> + <translation>Дали да покаже възможностите за контрол на монетите или не.</translation> + </message> + <message> <source>&OK</source> <translation>ОК</translation> </message> @@ -934,6 +966,10 @@ <translation>Текущата информация на екрана може да не е актуална. Вашият портфейл ще се синхронизира автоматично с мрежата на Биткоин, щом поне една връзката с нея се установи; този процес все още не е приключил.</translation> </message> <message> + <source>Watch-only:</source> + <translation>В наблюдателен режим:</translation> + </message> + <message> <source>Available:</source> <translation>Налично:</translation> </message> @@ -950,6 +986,10 @@ <translation>Неразвит:</translation> </message> <message> + <source>Mined balance that has not yet matured</source> + <translation>Миниран баланс,който все още не се е развил</translation> + </message> + <message> <source>Balances</source> <translation>Баланс</translation> </message> @@ -969,22 +1009,50 @@ <source>Recent transactions</source> <translation>Скорошни транзакции</translation> </message> - <message> - <source>out of sync</source> - <translation>несинхронизиран</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> <message> + <source>URI handling</source> + <translation>Справяне с URI</translation> + </message> + <message> + <source>Invalid payment address %1</source> + <translation>Невалиден адрес на плащане %1</translation> + </message> + <message> + <source>Payment request rejected</source> + <translation>Заявката за плащане беше отхвърлена</translation> + </message> + <message> + <source>Payment request network doesn't match client network.</source> + <translation>Мрежата от която се извършва заявката за плащане не съвпада с мрежата на клиента.</translation> + </message> + <message> <source>Requested payment amount of %1 is too small (considered dust).</source> <translation>Заявената сума за плащане: %1 е твърде малка (счита се за отпадък)</translation> </message> <message> + <source>Payment request error</source> + <translation>Възникна грешка по време назаявката за плащане</translation> + </message> + <message> + <source>Cannot start bitcoin: click-to-pay handler</source> + <translation>Биткойн не можe да се стартира: click-to-pay handler</translation> + </message> + <message> + <source>Payment request file handling</source> + <translation>Файл за справяне със заявки</translation> + </message> + <message> <source>Refund from %1</source> <translation>Възстановяване на сума от %1</translation> </message> <message> + <source>Payment request DoS protection</source> + <translation>Дос защита на заявката за плащане</translation> + </message> + <message> <source>Error communicating with %1: %2</source> <translation>Грешка при комуникацията с %1: %2</translation> </message> @@ -1019,6 +1087,14 @@ <translation>Сума</translation> </message> <message> + <source>Enter a Bitcoin address (e.g. %1)</source> + <translation>Въведете Биткойн адрес (например: %1)</translation> + </message> + <message> + <source>%1 d</source> + <translation>%1 ден</translation> + </message> + <message> <source>%1 h</source> <translation>%1 час</translation> </message> @@ -1149,6 +1225,30 @@ <translation>Услуги</translation> </message> <message> + <source>Starting Height</source> + <translation>Стартова височина</translation> + </message> + <message> + <source>Connection Time</source> + <translation>Продължителност на връзката</translation> + </message> + <message> + <source>Last Send</source> + <translation>Изпратени за последно</translation> + </message> + <message> + <source>Last Receive</source> + <translation>Получени за последно</translation> + </message> + <message> + <source>Bytes Sent</source> + <translation>Изпратени байтове</translation> + </message> + <message> + <source>Bytes Received</source> + <translation>Получени байтове</translation> + </message> + <message> <source>Ping Time</source> <translation>Време за отговор</translation> </message> @@ -1220,7 +1320,31 @@ <source>%1 GB</source> <translation>%1 Гигабайт</translation> </message> - </context> + <message> + <source>via %1</source> + <translation>посредством %1</translation> + </message> + <message> + <source>never</source> + <translation>Никога</translation> + </message> + <message> + <source>Inbound</source> + <translation>Входящи</translation> + </message> + <message> + <source>Outbound</source> + <translation>Изходящи</translation> + </message> + <message> + <source>Unknown</source> + <translation>Неизвестен</translation> + </message> + <message> + <source>Fetching...</source> + <translation>Прихващане...</translation> + </message> +</context> <context> <name>ReceiveCoinsDialog</name> <message> @@ -1409,6 +1533,14 @@ <translation>Ако тази опция е активирана,но адресът на промяна е празен или невалиден,промяната ще бъде изпратена на новосъздаден адрес.</translation> </message> <message> + <source>Transaction Fee:</source> + <translation>Такса за транзакцията:</translation> + </message> + <message> + <source>Choose...</source> + <translation>Избери...</translation> + </message> + <message> <source>per kilobyte</source> <translation>за килобайт</translation> </message> @@ -1421,10 +1553,22 @@ <translation>Препоръчителна:</translation> </message> <message> + <source>Custom:</source> + <translation>По избор:</translation> + </message> + <message> <source>Confirmation time:</source> <translation>Време за потвърждение:</translation> </message> <message> + <source>normal</source> + <translation>нормален</translation> + </message> + <message> + <source>fast</source> + <translation>бърз</translation> + </message> + <message> <source>Send to multiple recipients at once</source> <translation>Изпращане към повече от един получател</translation> </message> @@ -1437,6 +1581,10 @@ <translation>Изчисти всички полета от формуляра.</translation> </message> <message> + <source>Dust:</source> + <translation>Прах:</translation> + </message> + <message> <source>Clear &All</source> <translation>&Изчисти</translation> </message> @@ -1525,6 +1673,10 @@ <translation>Внимание:Неизвестен адрес за промяна</translation> </message> <message> + <source>Copy dust</source> + <translation>Копирай прахта:</translation> + </message> + <message> <source>Are you sure you want to send?</source> <translation>Наистина ли искате да изпратите?</translation> </message> @@ -1583,7 +1735,11 @@ <source>Pay To:</source> <translation>Плащане на:</translation> </message> - </context> + <message> + <source>Memo:</source> + <translation>Бележка:</translation> + </message> +</context> <context> <name>ShutdownWindow</name> <message> @@ -2167,14 +2323,30 @@ <translation>Внимание: -paytxfee е с мното голяма зададена стойност! Това е транзакционната такса, която ще платите ако направите транзакция.</translation> </message> <message> + <source>Whitelist peers connecting from the given netmask or IP address. Can be specified multiple times.</source> + <translation>Сложете в бял списък пиъри,свързващи се от дадената интернет маска или айпи адрес.Може да бъде заложено неколкократно.</translation> + </message> + <message> <source>(default: 1)</source> <translation>(по подразбиране 1)</translation> </message> <message> + <source><category> can be:</source> + <translation><category> може да бъде:</translation> + </message> + <message> <source>Connection options:</source> <translation>Настройки на връзката:</translation> </message> <message> + <source>Do you want to rebuild the block database now?</source> + <translation>Желаете ли да пресъздадете базата данни с блокове сега?</translation> + </message> + <message> + <source>Error initializing block database</source> + <translation>Грешка в пускането на базата данни с блокове</translation> + </message> + <message> <source>Error: Disk space is low!</source> <translation>Грешка: мястото на диска е малко!</translation> </message> @@ -2199,6 +2371,14 @@ <translation>Настройки на портфейла:</translation> </message> <message> + <source>Set the number of threads for coin generation if enabled (-1 = all cores, default: %d)</source> + <translation>Заложете броя на нишки за генерация на монети ако е включено(-1 = всички ядра, по подразбиране: %d)</translation> + </message> + <message> + <source>Warning: -maxtxfee is set very high! Fees this large could be paid on a single transaction.</source> + <translation>Внимание: -maxtxfee има много висока стойност! Толкова високи такси могат да бъдат заплатени на една транзакция.</translation> + </message> + <message> <source>Choose data directory on startup (default: 0)</source> <translation>Изберете директория при стартиране на програмата.( настройка по подразбиране:0)</translation> </message> @@ -2303,6 +2483,14 @@ <translation>Назовете конфигурационен файл(по подразбиране %s)</translation> </message> <message> + <source>Specify connection timeout in milliseconds (minimum: 1, default: %d)</source> + <translation>Задайте време на изключване при проблеми със свързването в милисекунди(минимум:1, по подразбиране %d)</translation> + </message> + <message> + <source>Specify pid file (default: %s)</source> + <translation>Задайте pid файл(по подразбиране: %s)</translation> + </message> + <message> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Невалидна сума за -paytxfee=<amount>: '%s'</translation> </message> diff --git a/src/qt/locale/bitcoin_ca.ts b/src/qt/locale/bitcoin_ca.ts index 37d1dfc9eb..f7d97eb061 100644 --- a/src/qt/locale/bitcoin_ca.ts +++ b/src/qt/locale/bitcoin_ca.ts @@ -1137,10 +1137,6 @@ <source>Current total balance in watch-only addresses</source> <translation>Balanç total actual en adreces de només lectura</translation> </message> - <message> - <source>out of sync</source> - <translation>Fora de sincronia</translation> - </message> </context> <context> <name>PaymentServer</name> diff --git a/src/qt/locale/bitcoin_ca@valencia.ts b/src/qt/locale/bitcoin_ca@valencia.ts index 0a52b1bc9a..c68cfb686a 100644 --- a/src/qt/locale/bitcoin_ca@valencia.ts +++ b/src/qt/locale/bitcoin_ca@valencia.ts @@ -1073,10 +1073,6 @@ <source>Current total balance in watch-only addresses</source> <translation>Balanç total actual en adreces de només lectura</translation> </message> - <message> - <source>out of sync</source> - <translation>Fora de sincronia</translation> - </message> </context> <context> <name>PaymentServer</name> diff --git a/src/qt/locale/bitcoin_ca_ES.ts b/src/qt/locale/bitcoin_ca_ES.ts index 3baa6e00cd..cd6aa96d34 100644 --- a/src/qt/locale/bitcoin_ca_ES.ts +++ b/src/qt/locale/bitcoin_ca_ES.ts @@ -1137,10 +1137,6 @@ <source>Current total balance in watch-only addresses</source> <translation>Balanç total actual en adreces de només lectura</translation> </message> - <message> - <source>out of sync</source> - <translation>Fora de sincronia</translation> - </message> </context> <context> <name>PaymentServer</name> diff --git a/src/qt/locale/bitcoin_cs.ts b/src/qt/locale/bitcoin_cs.ts index b4d9ca146d..47464b7a53 100644 --- a/src/qt/locale/bitcoin_cs.ts +++ b/src/qt/locale/bitcoin_cs.ts @@ -1137,10 +1137,6 @@ <source>Current total balance in watch-only addresses</source> <translation>Aktuální stav účtu sledovaných adres</translation> </message> - <message> - <source>out of sync</source> - <translation>nesynchronizováno</translation> - </message> </context> <context> <name>PaymentServer</name> diff --git a/src/qt/locale/bitcoin_da.ts b/src/qt/locale/bitcoin_da.ts index 6979124615..1467791047 100644 --- a/src/qt/locale/bitcoin_da.ts +++ b/src/qt/locale/bitcoin_da.ts @@ -1211,10 +1211,6 @@ <source>Current total balance in watch-only addresses</source> <translation>Nuværende totalsaldo på kigge-adresser</translation> </message> - <message> - <source>out of sync</source> - <translation>ikke synkroniseret</translation> - </message> </context> <context> <name>PaymentServer</name> @@ -2922,7 +2918,7 @@ </message> <message> <source>Do you want to rebuild the block database now?</source> - <translation>Ønsker du at genbygge blokdatabasen nu?</translation> + <translation>Ønsker du at genopbygge blokdatabasen nu?</translation> </message> <message> <source>Error initializing block database</source> @@ -3162,7 +3158,7 @@ for eksempel: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com </message> <message> <source>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source> - <translation>Du er nødt til at genbygge databasen ved hjælp af -reindex for at gå tilbage til ikke-beskåret tilstand. Dette vil downloade hele blokkæden igen</translation> + <translation>Du er nødt til at genopbygge databasen ved hjælp af -reindex for at gå tilbage til ikke-beskåret tilstand. Dette vil downloade hele blokkæden igen</translation> </message> <message> <source>(default: %u)</source> @@ -3281,6 +3277,10 @@ for eksempel: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com <translation>Slør tilfældigt 1 ud af hver <n> netværksbeskeder</translation> </message> <message> + <source>Rebuild block chain index from current blk000??.dat files on startup</source> + <translation>Genopbyg blokkædeindeks fra nuværende blk000??.dat-filer ved opstart</translation> + </message> + <message> <source>Send trace/debug info to console instead of debug.log file</source> <translation>Send sporings-/fejlsøgningsinformation til konsollen i stedet for debug.log filen</translation> </message> diff --git a/src/qt/locale/bitcoin_de.ts b/src/qt/locale/bitcoin_de.ts index 928b72fae8..ab0367dbab 100644 --- a/src/qt/locale/bitcoin_de.ts +++ b/src/qt/locale/bitcoin_de.ts @@ -168,6 +168,10 @@ <translation>Sind Sie sich sicher, dass Sie Ihre Wallet verschlüsseln möchten?</translation> </message> <message> + <source>Bitcoin Core will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> + <translation>Bitcoin Core wird jetzt beendet, um den Verschlüsselungsprozess abzuschließen. Vergessen Sie nicht, dass eine Wallet-Verschlüsselung nicht vollständig vor Diebstahl Ihrer Bitcoins durch Schadsoftware schützen kann, die Ihren Computer infiziert.</translation> + </message> + <message> <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> <translation>WICHTIG: Alle vorherigen Wallet-Sicherungen sollten durch die neu erzeugte, verschlüsselte Wallet ersetzt werden. Aus Sicherheitsgründen werden vorherige Sicherungen der unverschlüsselten Wallet nutzlos, sobald Sie die neue, verschlüsselte Wallet verwenden.</translation> </message> @@ -184,6 +188,10 @@ <translation>Geben Sie die neue Passphrase für die Wallet ein.<br>Bitte benutzen Sie eine Passphrase bestehend aus <b>zehn oder mehr zufälligen Zeichen</b> oder <b>acht oder mehr Wörtern</b>.</translation> </message> <message> + <source>Enter the old passphrase and new passphrase to the wallet.</source> + <translation>Geben Sie die alte und neue Wallet-Passphrase ein.</translation> + </message> + <message> <source>Wallet encryption failed</source> <translation>Wallet-Verschlüsselung fehlgeschlagen</translation> </message> @@ -392,7 +400,7 @@ </message> <message> <source>Modify configuration options for Bitcoin Core</source> - <translation>Konfigurationsoptionen für Bitcoin Core ändern</translation> + <translation>Konfiguration von Bitcoin Core bearbeiten</translation> </message> <message> <source>Show the list of used sending addresses and labels</source> @@ -423,6 +431,10 @@ <translation>Keine Blockquelle verfügbar...</translation> </message> <message numerus="yes"> + <source>Processed %n block(s) of transaction history.</source> + <translation><numerusform>%n Block des Transaktionsverlaufs verarbeitet.</numerusform><numerusform>%n Blöcke des Transaktionsverlaufs verarbeitet.</numerusform></translation> + </message> + <message numerus="yes"> <source>%n hour(s)</source> <translation><numerusform>%n Stunde</numerusform><numerusform>%n Stunden</numerusform></translation> </message> @@ -703,6 +715,18 @@ <translation>keine</translation> </message> <message> + <source>This label turns red if the transaction size is greater than 1000 bytes.</source> + <translation>Diese Bezeichnung wird rot, wenn die Transaktion größer als 1000 Byte ist.</translation> + </message> + <message> + <source>This label turns red if the priority is smaller than "medium".</source> + <translation>Diese Bezeichnung wird rot, wenn die Priorität niedriger als "mittel" ist.</translation> + </message> + <message> + <source>This label turns red if any recipient receives an amount smaller than %1.</source> + <translation>Diese Bezeichnung wird rot, wenn irgendein Empfänger einen Betrag kleiner als %1 erhält.</translation> + </message> + <message> <source>Can vary +/- %1 satoshi(s) per input.</source> <translation>Kann pro Eingabe um +/- %1 Satoshi(s) abweichen.</translation> </message> @@ -953,6 +977,14 @@ <translation>IP-Adresse des Proxies (z.B. IPv4: 127.0.0.1 / IPv6: ::1)</translation> </message> <message> + <source>Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</source> + <translation>Minimiert die Anwendung anstatt sie zu beenden wenn das Fenster geschlossen wird. Wenn dies aktiviert ist, müssen Sie die Anwendung über "Beenden" im Menü schließen.</translation> + </message> + <message> + <source>The user interface language can be set here. This setting will take effect after restarting Bitcoin Core.</source> + <translation>Legt die Sprache der Benutzeroberfläche fest. Diese Einstellung wird erst nach einem Neustart von Bitcoin Core aktiv.</translation> + </message> + <message> <source>Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</source> <translation>Externe URLs (z.B. ein Block-Explorer), die im Kontextmenü des Transaktionsverlaufs eingefügt werden. In der URL wird %s durch den Transaktionshash ersetzt. Bei Angabe mehrerer URLs müssen diese durch "|" voneinander getrennt werden.</translation> </message> @@ -978,11 +1010,11 @@ </message> <message> <source>Automatically start Bitcoin Core after logging in to the system.</source> - <translation>Bitcoin Core nach dem Anmelden am System automatisch starten.</translation> + <translation>Bitcoin Core nach der Anmeldung am System automatisch starten.</translation> </message> <message> <source>&Start Bitcoin Core on system login</source> - <translation>Bitcoin Core bei der Systemanmeldung &starten</translation> + <translation>&Bitcoin Core nach Systemanmeldung starten</translation> </message> <message> <source>(0 = auto, <0 = leave that many cores free)</source> @@ -1098,7 +1130,7 @@ </message> <message> <source>Client will be shut down. Do you want to proceed?</source> - <translation>Client wird heruntergefahren. Möchten Sie den Vorgang fortsetzen?</translation> + <translation>Client wird beendet. Möchten Sie den Vorgang fortsetzen?</translation> </message> <message> <source>This change would require a client restart.</source> @@ -1183,10 +1215,6 @@ <source>Current total balance in watch-only addresses</source> <translation>Aktueller Gesamtbetrag in beobachteten Adressen aus obigen Kategorien</translation> </message> - <message> - <source>out of sync</source> - <translation>nicht synchron</translation> - </message> </context> <context> <name>PaymentServer</name> @@ -1239,10 +1267,18 @@ <translation>Zahlungsanforderungsdatei kann nicht gelesen werden! Dies kann durch eine ungültige Zahlungsanforderungsdatei verursacht werden.</translation> </message> <message> + <source>Payment request expired.</source> + <translation>Zahlungsanforderung abgelaufen.</translation> + </message> + <message> <source>Unverified payment requests to custom payment scripts are unsupported.</source> <translation>Unverifizierte Zahlungsanforderungen an benutzerdefinierte Zahlungsskripte werden nicht unterstützt.</translation> </message> <message> + <source>Invalid payment request.</source> + <translation>Ungültige Zahlungsanforderung.</translation> + </message> + <message> <source>Refund from %1</source> <translation>Rücküberweisung von %1</translation> </message> @@ -1407,6 +1443,10 @@ <translation>Aktuelle Anzahl Blöcke</translation> </message> <message> + <source>Open the Bitcoin Core debug log file from the current data directory. This can take a few seconds for large log files.</source> + <translation>Öffnet die "Bitcoin Core"-Debugprotokolldatei aus dem aktuellen Datenverzeichnis. Dies kann bei großen Protokolldateien einige Sekunden dauern.</translation> + </message> + <message> <source>Received</source> <translation>Empfangen</translation> </message> @@ -1524,7 +1564,7 @@ </message> <message> <source>Welcome to the Bitcoin Core RPC console.</source> - <translation>Willkommen in der Bitcoin Core RPC-Konsole.</translation> + <translation>Willkommen in der "Bitcoin Core"-RPC-Konsole.</translation> </message> <message> <source>Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen.</source> @@ -1971,10 +2011,26 @@ <translation>Eine höhere Gebühr als %1 wird als unsinnig hohe Gebühr angesehen.</translation> </message> <message> + <source>Payment request expired.</source> + <translation>Zahlungsanforderung abgelaufen.</translation> + </message> + <message numerus="yes"> + <source>Estimated to begin confirmation within %n block(s).</source> + <translation><numerusform>Voraussichtlicher Beginn der Bestätigung innerhalb von %n Block.</numerusform><numerusform>Voraussichtlicher Beginn der Bestätigung innerhalb von %n Blöcken.</numerusform></translation> + </message> + <message> <source>Pay only the minimum fee of %1</source> <translation>Nur die minimale Gebühr in Höhe von %1 zahlen</translation> </message> <message> + <source>The recipient address is not valid. Please recheck.</source> + <translation>Die Zahlungsadresse ist ungültig, bitte nochmals überprüfen.</translation> + </message> + <message> + <source>Duplicate address found: addresses should only be used once each.</source> + <translation>Doppelte Adresse entdeckt: Adressen dürfen jeweils nur einmal vorkommen.</translation> + </message> + <message> <source>Warning: Invalid Bitcoin address</source> <translation>Warnung: Ungültige Bitcoin-Adresse</translation> </message> @@ -2046,14 +2102,26 @@ <translation>Diesen Eintrag entfernen</translation> </message> <message> + <source>The fee will be deducted from the amount being sent. The recipient will receive less bitcoins than you enter in the amount field. If multiple recipients are selected, the fee is split equally.</source> + <translation>Die Gebühr wird vom zu überweisenden Betrag abgezogen. Der Empfänger wird also weniger Bitcoins erhalten, als Sie im Betrags-Feld eingegeben haben. Falls mehrere Empfänger ausgewählt wurden, wird die Gebühr gleichmäßig verteilt.</translation> + </message> + <message> <source>S&ubtract fee from amount</source> - <translation>Gebühr von Betrag ab&ziehen</translation> + <translation>Gebühr vom Betrag ab&ziehen</translation> </message> <message> <source>Message:</source> <translation>Nachricht:</translation> </message> <message> + <source>This is an unauthenticated payment request.</source> + <translation>Dies ist keine beglaubigte Zahlungsanforderung.</translation> + </message> + <message> + <source>This is an authenticated payment request.</source> + <translation>Dies ist eine beglaubigte Zahlungsanforderung.</translation> + </message> + <message> <source>Enter a label for this address to add it to the list of used addresses</source> <translation>Adressbezeichnung eingeben, die dann zusammen mit der Adresse der Liste bereits verwendeter Adressen hinzugefügt wird.</translation> </message> @@ -2092,6 +2160,10 @@ <translation>Nachricht &signieren</translation> </message> <message> + <source>You can sign messages/agreements with your addresses to prove you can receive bitcoins sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to.</source> + <translation>Sie können Nachrichten/Vereinbarungen mit Hilfe Ihrer Adressen signieren, um zu beweisen, dass Sie Bitcoins empfangen können, die an diese Adressen überwiesen werden. Seien Sie vorsichtig und signieren Sie nichts Vages oder Willkürliches, um Ihre Indentität vor Phishingangriffen zu schützen. Signieren Sie nur vollständig-detaillierte Aussagen, mit denen Sie auch einverstanden sind.</translation> + </message> + <message> <source>The Bitcoin address to sign the message with</source> <translation>Die Bitcoin-Adresse mit der die Nachricht signiert wird</translation> </message> @@ -2144,6 +2216,10 @@ <translation>Nachricht &verifizieren</translation> </message> <message> + <source>Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction!</source> + <translation>Geben Sie die Zahlungsadresse des Empfängers, Nachricht (achten Sie darauf Zeilenumbrüche, Leerzeichen, Tabulatoren usw. exakt zu kopieren) und Signatur unten ein, um die Nachricht zu verifizieren. Vorsicht, interpretieren Sie nicht mehr in die Signatur hinein, als in der signierten Nachricht selber enthalten ist, um nicht von einem Man-in-the-middle-Angriff hinters Licht geführt zu werden. Beachten Sie dass dies nur beweißt, dass die signierende Partei über diese Adresse Überweisungen empfangen kann.</translation> + </message> + <message> <source>The Bitcoin address the message was signed with</source> <translation>Die Bitcoin-Adresse mit der die Nachricht signiert wurde</translation> </message> @@ -2495,6 +2571,10 @@ <translation>Zeigt an, ob eine beobachtete Adresse in diese Transaktion involviert ist.</translation> </message> <message> + <source>User-defined intent/purpose of the transaction.</source> + <translation>Benutzerdefinierte Absicht bzw. Verwendungszweck der Transaktion</translation> + </message> + <message> <source>Amount removed from or added to balance.</source> <translation>Der Betrag, der dem Kontostand abgezogen oder hinzugefügt wurde.</translation> </message> @@ -2737,6 +2817,10 @@ <translation>An die angegebene Adresse binden und immer abhören. Für IPv6 "[Host]:Port"-Notation verwenden</translation> </message> <message> + <source>Continuously rate-limit free transactions to <n>*1000 bytes per minute (default: %u)</source> + <translation>Durchgehend die Anzahl freier Transaktionen auf <n> * 1000 Byte pro Minute begrenzen (Standard: %u)</translation> + </message> + <message> <source>Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup</source> <translation>Alle Wallet-Transaktionen löschen und nur diese Teilbereiche der Blockkette durch -rescan beim Starten wiederherstellen</translation> </message> @@ -2757,6 +2841,14 @@ <translation>In diesem Modus legt -genproclimit fest, wie viele Blöcke sofort erzeugt werden.</translation> </message> <message> + <source>Maximum total fees to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)</source> + <translation>Maximale Gesamtgebühren je Wallet-Transaktion, ein zu niedriger Wert kann große Transaktionen abbrechen (Standard: %s)</translation> + </message> + <message> + <source>Reduce storage requirements by pruning (deleting) old blocks. This mode disables wallet support and is incompatible with -txindex. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> + <translation>Speicherplatzanforderung durch kürzen (löschen) alter Blöcke reduzieren. Dieser Modus deaktiviert die Wallet-Unterstützung und ist nicht mit -txindex kompatibel. Warnung: Die Umkehr dieser Einstellung erfordert das erneute Herunterladen der gesamten Blockkette. (Standard: 0 = deaktiviert das Kürzen von Blöcken, >%u = Zielgröße in MiB, die für Blockdateien verwendet werden darf)</translation> + </message> + <message> <source>Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)</source> <translation>Maximale Anzahl an Skript-Verifizierungs-Threads festlegen (%u bis %d, 0 = automatisch, <0 = so viele Kerne frei lassen, Standard: %d)</translation> </message> @@ -2885,6 +2977,14 @@ <translation>Nur zu Knoten des Netzwerktyps <net> verbinden (ipv4, ipv6 oder onion)</translation> </message> <message> + <source>Prune cannot be configured with a negative value.</source> + <translation>Kürzungsmodus kann nicht mit einem negativen Wert konfiguriert werden.</translation> + </message> + <message> + <source>Prune mode is incompatible with -txindex.</source> + <translation>Kürzungsmodus ist nicht mit -txindex kompatibel.</translation> + </message> + <message> <source>Set database cache size in megabytes (%d to %d, default: %d)</source> <translation>Größe des Datenbankcaches in Megabyte festlegen (%d bis %d, Standard: %d)</translation> </message> @@ -2921,6 +3021,10 @@ <translation>Wallet-Optionen:</translation> </message> <message> + <source>Warning: This version is obsolete; upgrade required!</source> + <translation>Warnung: Diese Version is veraltet, Aktualisierung erforderlich!</translation> + </message> + <message> <source>You need to rebuild the database using -reindex to change -txindex</source> <translation>Sie müssen die Datenbank mit Hilfe von -reindex neu aufbauen, um -txindex zu verändern</translation> </message> @@ -2953,6 +3057,10 @@ <translation>Neue Dateien mit Standard-Systemrechten erzeugen, anstatt mit umask 077 (nur mit deaktivierter Walletfunktion nutzbar)</translation> </message> <message> + <source>Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)</source> + <translation>Eigene IP-Adressen ermitteln (Standard: 1, wenn abgehört wird und nicht -externalip oder -proxy)</translation> + </message> + <message> <source>Error: Listening for incoming connections failed (listen returned error %s)</source> <translation>Fehler: Abhören nach eingehenden Verbindungen fehlgeschlagen (listen meldete Fehler %s)</translation> </message> @@ -2985,10 +3093,22 @@ <translation>Maximale Datengröße in "Data Carrier"-Transaktionen die weitergeleitet und erarbeitet werden (Standard: %u)</translation> </message> <message> + <source>Prune configured below the minimum of %d MB. Please use a higher number.</source> + <translation>Kürzungsmodus wurde kleiner als das Minimum in Höhe von %d MiB konfiguriert. Bitte verwenden Sie einen größeren Wert.</translation> + </message> + <message> <source>Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect)</source> <translation>Adressen von Gegenstellen via DNS-Namensauflösung finden, falls zu wenige Adressen verfügbar sind (Standard: 1, außer bei -connect)</translation> </message> <message> + <source>Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)</source> + <translation>Zufällige Anmeldedaten für jede Proxyverbindung verwenden. Dies aktiviert Tor-Datenflussisolation (Standard: %u)</translation> + </message> + <message> + <source>Require high priority for relaying free or low-fee transactions (default: %u)</source> + <translation>Hohe Priorität zum Weiterleiten von freien bzw. Transaktionen mit niedrigen Gebühren voraussetzen (Standard: %u)</translation> + </message> + <message> <source>Set maximum size of high-priority/low-fee transactions in bytes (default: %d)</source> <translation>Maximale Größe in Byte von "high-priority/low-fee"-Transaktionen festlegen (Standard: %d)</translation> </message> @@ -2997,6 +3117,10 @@ <translation>Maximale Anzahl an Threads zur Bitcoinerzeugung, wenn aktiviert, festlegen (-1 = alle Kerne, Standard: %d)</translation> </message> <message> + <source>The transaction amount is too small to send after the fee has been deducted</source> + <translation>Der Transaktionsbetrag ist zum senden zu niedrig, nachdem die Gebühr abgezogen wurde.</translation> + </message> + <message> <source>This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit <https://www.openssl.org/> and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard.</source> <translation>Dieses Produkt enthält Software, die vom OpenSSL-Projekt zur Verwendung im OpenSSL-Toolkit <https://www.openssl.org/> entwickelt wird, sowie von Eric Young geschriebene kryptographische Software und von Thomas Bernard geschriebene UPnP-Software.</translation> </message> @@ -3037,6 +3161,10 @@ Beispiel: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com <translation>Erlaubte Gegenstellen werden nicht für DoS-Attacken gesperrt und ihre Transkationen werden immer weitergeleitet, auch wenn sie sich bereits im Speicherpool befinden, was z.B. für Gateways sinnvoll ist.</translation> </message> <message> + <source>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source> + <translation>Sie müssen die Datenbank mit Hilfe von -reindex neu aufbauen, um zum ungekürzten Modus zurückzukehren. Dies erfordert, dass die gesamte Blockkette erneut heruntergeladen wird.</translation> + </message> + <message> <source>(default: %u)</source> <translation>(Standard: %u)</translation> </message> @@ -3045,6 +3173,18 @@ Beispiel: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com <translation>Öffentliche REST-Anfragen annehmen (Standard: %u)</translation> </message> <message> + <source>Activating best chain...</source> + <translation>Aktiviere beste Blockkette...</translation> + </message> + <message> + <source>Allow self signed root certificates (default: 0)</source> + <translation>Selbstunterschriebene Stammzertifikate erlauben (Standard: 0)</translation> + </message> + <message> + <source>Can't run with a wallet in prune mode.</source> + <translation>Eine Wallet kann im Kürzungsmodus nicht verwendet werden.</translation> + </message> + <message> <source>Cannot resolve -whitebind address: '%s'</source> <translation>Kann Adresse in -whitebind nicht auflösen: '%s'</translation> </message> @@ -3141,6 +3281,10 @@ Beispiel: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com <translation>Zufällig eine von <n> Netzwerknachrichten verwürfeln</translation> </message> <message> + <source>Rebuild block chain index from current blk000??.dat files on startup</source> + <translation>Blockkettenindex aus aktuellen Dateien blk000??.dat beim Starten wiederaufbauen</translation> + </message> + <message> <source>Send trace/debug info to console instead of debug.log file</source> <translation>Rückverfolgungs- und Debuginformationen an die Konsole senden, anstatt sie in debug.log zu schreiben</translation> </message> @@ -3177,6 +3321,10 @@ Beispiel: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com <translation>Minimiert starten</translation> </message> <message> + <source>The transaction amount is too small to pay the fee</source> + <translation>Der Transaktionsbetrag ist zu niedrig, um die Gebühr zu bezahlen.</translation> + </message> + <message> <source>This is experimental software.</source> <translation>Dies ist experimentelle Software.</translation> </message> @@ -3361,6 +3509,10 @@ Beispiel: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com <translation>Maximal <n> Verbindungen zu Gegenstellen aufrechterhalten (Standard: %u)</translation> </message> <message> + <source>Make the wallet broadcast transactions</source> + <translation>Die Wallet soll Transaktionen übertragen/broadcasten</translation> + </message> + <message> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)</source> <translation>Maximale Größe des Empfangspuffers pro Verbindung, <n> * 1000 Byte (Standard: %u)</translation> </message> diff --git a/src/qt/locale/bitcoin_el_GR.ts b/src/qt/locale/bitcoin_el_GR.ts index 5cabe43032..a4c95857ba 100644 --- a/src/qt/locale/bitcoin_el_GR.ts +++ b/src/qt/locale/bitcoin_el_GR.ts @@ -2,6 +2,10 @@ <context> <name>AddressBookPage</name> <message> + <source>Right-click to edit address or label</source> + <translation>Δεξί-κλικ για επεξεργασία της διεύθυνσης ή της ετικέτας</translation> + </message> + <message> <source>Create a new address</source> <translation>Δημιουργία νέας διεύθυνσης</translation> </message> @@ -881,10 +885,18 @@ <translation>Διεύθυνση IP του διαμεσολαβητή (π.χ. 127.0.0.1 / IPv6: ::1)</translation> </message> <message> + <source>Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</source> + <translation>URLs από τρίτους (π.χ. ένας εξερευνητής μπλοκ) τα οποία εμφανίζονται στην καρτέλα συναλλαγών ως στοιχεία μενού. Το %s στα URL αντικαθιστάται από την τιμή της κατατεμαχισμένης συναλλαγής.</translation> + </message> + <message> <source>Third party transaction URLs</source> <translation>Διευθύνσεις τρίτων συναλλαγών.</translation> </message> <message> + <source>Active command-line options that override above options:</source> + <translation>Ενεργές επιλογές γραμμής-εντολών που παρακάμπτουν τις παραπάνω επιλογές:</translation> + </message> + <message> <source>Reset all client options to default.</source> <translation>Επαναφορα όλων των επιλογων του πελάτη σε default.</translation> </message> @@ -897,6 +909,10 @@ <translation>&Δίκτυο</translation> </message> <message> + <source>(0 = auto, <0 = leave that many cores free)</source> + <translation>(0 = αυτόματο, <0 = ελεύθεροι πυρήνες)</translation> + </message> + <message> <source>W&allet</source> <translation>Π&ορτοφόλι</translation> </message> @@ -909,6 +925,14 @@ <translation>Επιλογή κατα πόσο να αναδείχνονται οι δυνατότητες ελέγχου κερμάτων.</translation> </message> <message> + <source>If you disable the spending of unconfirmed change, the change from a transaction cannot be used until that transaction has at least one confirmation. This also affects how your balance is computed.</source> + <translation>Εάν απενεργοποιήσετε το ξόδεμα μη επικυρωμένων ρέστων, τα ρέστα από μια συναλλαγή δεν μπορούν να χρησιμοποιηθούν έως ότου αυτή η συναλλαγή έχει έστω μια επικύρωση. Αυτό επίσης επηρεάζει το πως υπολογίζεται το υπόλοιπό σας.</translation> + </message> + <message> + <source>&Spend unconfirmed change</source> + <translation>&Ξόδεμα μη επικυρωμένων ρέστων</translation> + </message> + <message> <source>Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</source> <translation>Αυτόματο άνοιγμα των θυρών Bitcoin στον δρομολογητή. Λειτουργεί μόνο αν ο δρομολογητής σας υποστηρίζει τη λειτουργία UPnP.</translation> </message> @@ -1057,6 +1081,10 @@ <translation>Το τρέχον συνολικό υπόλοιπο</translation> </message> <message> + <source>Your current balance in watch-only addresses</source> + <translation>Το τρέχον υπόλοιπο σας σε διευθύνσεις παρακολούθησης μόνο</translation> + </message> + <message> <source>Spendable:</source> <translation>Ξοδεμένα:</translation> </message> @@ -1065,8 +1093,16 @@ <translation>Πρόσφατες συναλλαγές</translation> </message> <message> - <source>out of sync</source> - <translation>εκτός συγχρονισμού</translation> + <source>Unconfirmed transactions to watch-only addresses</source> + <translation>Μη επικυρωμένες συναλλαγές σε διευθύνσεις παρακολούθησης μόνο</translation> + </message> + <message> + <source>Mined balance in watch-only addresses that has not yet matured</source> + <translation>Εξορυγμένο υπόλοιπο σε διευθύνσεις παρακολούθησης μόνο που δεν έχει ωριμάσει ακόμα</translation> + </message> + <message> + <source>Current total balance in watch-only addresses</source> + <translation>Το τρέχον συνολικό υπόλοιπο σε διευθύνσεις παρακολούθησης μόνο</translation> </message> </context> <context> @@ -1088,6 +1124,10 @@ <translation>Η αίτηση πληρωμής δεν έχει αρχίζει ακόμα.</translation> </message> <message> + <source>Requested payment amount of %1 is too small (considered dust).</source> + <translation>Το ζητούμενο ποσό πληρωμής του %1 είναι πολύ μικρό (θεωρείται σκόνη)</translation> + </message> + <message> <source>Payment request error</source> <translation>Σφάλμα αιτήματος πληρωμής</translation> </message> @@ -1108,6 +1148,18 @@ <translation>Επιστροφή ποσού από %1</translation> </message> <message> + <source>Error communicating with %1: %2</source> + <translation>Σφάλμα επικοινωνίας με %1: %2</translation> + </message> + <message> + <source>Payment request cannot be parsed!</source> + <translation>Η αίτηση πληρωμής δεν μπορεί να αναλυθεί!</translation> + </message> + <message> + <source>Bad response from server %1</source> + <translation>Κακή απάντηση από διακομιστή %1</translation> + </message> + <message> <source>Payment acknowledged</source> <translation>Πληρωμή αναγνωρίστηκε</translation> </message> @@ -1212,6 +1264,10 @@ <translation>Χρησιμοποιηση της OpenSSL εκδοσης</translation> </message> <message> + <source>Using BerkeleyDB version</source> + <translation>Χρήση BerkeleyDB έκδοσης</translation> + </message> + <message> <source>Startup time</source> <translation>Χρόνος εκκίνησης</translation> </message> @@ -1240,6 +1296,18 @@ <translation>Παραλήφθησαν</translation> </message> <message> + <source>Sent</source> + <translation>Αποστολή</translation> + </message> + <message> + <source>&Peers</source> + <translation>&Χρήστες</translation> + </message> + <message> + <source>Select a peer to view detailed information.</source> + <translation>Επιλέξτε ένα χρήστη για να δείτε αναλυτικές πληροφορίες.</translation> + </message> + <message> <source>Version</source> <translation>Έκδοση</translation> </message> @@ -1248,6 +1316,38 @@ <translation>Υπηρεσίες</translation> </message> <message> + <source>Starting Height</source> + <translation>Αρχικό ύψος</translation> + </message> + <message> + <source>Sync Height</source> + <translation>Ύψος συγχονισμού</translation> + </message> + <message> + <source>Ban Score</source> + <translation>Σκορ αποκλησμού</translation> + </message> + <message> + <source>Connection Time</source> + <translation>Χρόνος σύνδεσης</translation> + </message> + <message> + <source>Last Send</source> + <translation>Τελευταία αποστολή</translation> + </message> + <message> + <source>Last Receive</source> + <translation>Τελευταία λήψη</translation> + </message> + <message> + <source>Bytes Sent</source> + <translation>Σταλθέντα bytes</translation> + </message> + <message> + <source>Bytes Received</source> + <translation>Ληφθέντα bytes</translation> + </message> + <message> <source>Ping Time</source> <translation>Χρόνος καθυστέρησης</translation> </message> @@ -1339,7 +1439,11 @@ <source>Unknown</source> <translation>Άγνωστο(α)</translation> </message> - </context> + <message> + <source>Fetching...</source> + <translation>Ανάκτηση...</translation> + </message> +</context> <context> <name>ReceiveCoinsDialog</name> <message> @@ -1355,6 +1459,10 @@ <translation>&Μήνυμα:</translation> </message> <message> + <source>R&euse an existing receiving address (not recommended)</source> + <translation>Ε&παναχρησιμοποίηση υπάρχουσας διεύθυνσης λήψης (δεν συνιστάται)</translation> + </message> + <message> <source>Clear all fields of the form.</source> <translation>Καθαρισμός όλων των πεδίων της φόρμας.</translation> </message> @@ -1371,6 +1479,10 @@ <translation>Εμφάνιση</translation> </message> <message> + <source>Remove the selected entries from the list</source> + <translation>Αφαίρεση επιλεγμένων καταχωρίσεων από τη λίστα</translation> + </message> + <message> <source>Remove</source> <translation>Αφαίρεση</translation> </message> @@ -1398,10 +1510,18 @@ <translation>Αντιγραφη της επιλεγμενης διεύθυνσης στο πρόχειρο του συστηματος</translation> </message> <message> + <source>Copy &Address</source> + <translation>Αντιγραφή &Διεύθυνσης</translation> + </message> + <message> <source>&Save Image...</source> <translation>&Αποθήκευση εικόνας...</translation> </message> <message> + <source>Request payment to %1</source> + <translation>Αίτηση πληρωμής για %1</translation> + </message> + <message> <source>Payment information</source> <translation>Πληροφορίες πληρωμής</translation> </message> @@ -1476,6 +1596,10 @@ <translation>Χαρακτηρηστικά επιλογής κερμάτων</translation> </message> <message> + <source>Inputs...</source> + <translation>Εισροές...</translation> + </message> + <message> <source>automatically selected</source> <translation>επιλεγμένο αυτόματα</translation> </message> @@ -1512,6 +1636,14 @@ <translation>Ρέστα:</translation> </message> <message> + <source>If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</source> + <translation>Όταν ενεργό, αλλά η διεύθυνση ρέστων είναι κενή ή άκυρη, τα ρέστα θα σταλούν σε μία πρόσφατα δημιουργημένη διεύθυνση.</translation> + </message> + <message> + <source>Custom change address</source> + <translation>Προσαρμοσμένη διεύθυνση ρέστων</translation> + </message> + <message> <source>Transaction Fee:</source> <translation>Τέλος συναλλαγής:</translation> </message> @@ -1520,10 +1652,34 @@ <translation>Επιλογή...</translation> </message> <message> + <source>per kilobyte</source> + <translation>ανά kilobyte</translation> + </message> + <message> + <source>Recommended:</source> + <translation>Προτεινόμενο: </translation> + </message> + <message> + <source>Custom:</source> + <translation>Προσαρμογή:</translation> + </message> + <message> + <source>Confirmation time:</source> + <translation>Χρόνος επικύρωσης:</translation> + </message> + <message> + <source>normal</source> + <translation>κανονικό</translation> + </message> + <message> <source>fast</source> <translation>Γρήγορο</translation> </message> <message> + <source>(confirmation may take longer)</source> + <translation>(η επικύρωση ίσως χρειαστεί περισσότερο χρόνο)</translation> + </message> + <message> <source>Send to multiple recipients at once</source> <translation>Αποστολή σε πολλούς αποδέκτες ταυτόχρονα</translation> </message> @@ -1659,6 +1815,14 @@ <translation>Επιλογή διεύθυνσης που έχει ήδη χρησιμοποιηθεί</translation> </message> <message> + <source>This is a normal payment.</source> + <translation>Αυτή είναι μια απλή πληρωμή.</translation> + </message> + <message> + <source>The Bitcoin address to send the payment to</source> + <translation>Η διεύθυνση Bitcoin που θα σταλεί η πληρωμή</translation> + </message> + <message> <source>Alt+A</source> <translation>Alt+A</translation> </message> @@ -1679,6 +1843,10 @@ <translation>Μήνυμα:</translation> </message> <message> + <source>Enter a label for this address to add it to the list of used addresses</source> + <translation>Εισάγεται μία ετικέτα για αυτή την διεύθυνση για να προστεθεί στη λίστα με τις χρησιμοποιημένες διευθύνσεις</translation> + </message> + <message> <source>Pay To:</source> <translation>Πληρωμή σε:</translation> </message> @@ -2191,6 +2359,10 @@ <translation>Επιτυχής εξαγωγή</translation> </message> <message> + <source>The transaction history was successfully saved to %1.</source> + <translation>Το ιστορικό συναλλαγών αποθηκεύτηκε επιτυχώς στο %1.</translation> + </message> + <message> <source>Comma separated file (*.csv)</source> <translation>Αρχείο οριοθετημένο με κόμματα (*.csv)</translation> </message> @@ -2229,7 +2401,11 @@ </context> <context> <name>UnitDisplayStatusBarControl</name> - </context> + <message> + <source>Unit to show amounts in. Click to select another unit.</source> + <translation>Μονάδα μέτρησης προβολής ποσών. Κάντε κλικ για επιλογή άλλης μονάδας.</translation> + </message> +</context> <context> <name>WalletFrame</name> <message> @@ -2267,6 +2443,10 @@ <translation>Αποτυχία κατά τη δημιουργία αντιγράφου</translation> </message> <message> + <source>There was an error trying to save the wallet data to %1.</source> + <translation>Παρουσιάστηκε σφάλμα κατά την αποθήκευση των δεδομένων πορτοφολιού στο %1.</translation> + </message> + <message> <source>The wallet data was successfully saved to %1.</source> <translation>Τα δεδομένα πορτοφολιού αποθηκεύτηκαν με επιτυχία στο %1.</translation> </message> @@ -2314,6 +2494,10 @@ <translation>Αποθηκευση σε συγκεκριμένη διεύθυνση. Χρησιμοποιήστε τα πλήκτρα [Host] : συμβολισμός θύρα για IPv6</translation> </message> <message> + <source>Enter regression test mode, which uses a special chain in which blocks can be solved instantly.</source> + <translation>Εισαγωγή δοκιμαστικής λειτουργίας παλινδρόμησης, που χρησιμοποιεί μια ειδική αλυσίδα στην οποία τα μπλοκ επιλύονται στιγμιαία.</translation> + </message> + <message> <source>Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)</source> <translation>Εκτέλεσε την εντολή όταν το καλύτερο μπλοκ αλλάξει(%s στην εντολή αντικαθίσταται από το hash του μπλοκ)</translation> </message> @@ -2402,10 +2586,18 @@ <translation>Δεν ειναι αρκετες περιγραφες αρχείων διαθέσιμες.</translation> </message> <message> + <source>Only connect to nodes in network <net> (ipv4, ipv6 or onion)</source> + <translation>Μόνο σύνδεση σε κόμβους του δικτύου <net> (ipv4, ipv6 ή onion)</translation> + </message> + <message> <source>Specify wallet file (within data directory)</source> <translation>Επιλέξτε αρχείο πορτοφολιού (μέσα απο κατάλογο δεδομένων)</translation> </message> <message> + <source>This is intended for regression testing tools and app development.</source> + <translation>Αυτό προορίζεται για εργαλεία δοκιμών παλινδρόμησης και την ανάπτυξη εφαρμογών.</translation> + </message> + <message> <source>Verifying blocks...</source> <translation>Επαλήθευση των μπλοκ... </translation> </message> @@ -2414,6 +2606,10 @@ <translation>Επαλήθευση πορτοφολιου... </translation> </message> <message> + <source>Wallet %s resides outside data directory %s</source> + <translation>Το πορτοφόλι %s βρίσκεται έξω από το φάκελο δεδομένων %s</translation> + </message> + <message> <source>Wallet options:</source> <translation>Επιλογές πορτοφολιού:</translation> </message> @@ -2426,6 +2622,10 @@ <translation>Αδυναμία κλειδώματος του φακέλου δεδομένων %s. Πιθανώς το Bitcoin να είναι ήδη ενεργό.</translation> </message> <message> + <source>Warning: Please check that your computer's date and time are correct! If your clock is wrong Bitcoin Core will not work properly.</source> + <translation>Προειδοποίηση: Παρακαλώ ελέγξτε ότι η ημερομηνία και ώρα του υπολογιστή σας είναι σωστά ρυθμισμένες! Εάν το ρολόι σας είναι λάθος το Bitcoin Core δεν θα λειτουργήσει σωστά. </translation> + </message> + <message> <source>Choose data directory on startup (default: 0)</source> <translation>Επιλογή φακέλου δεδομένων στην εκκίνηση (προεπιλεγμένο: 0)</translation> </message> @@ -2442,6 +2642,10 @@ <translation>Σφάλμα φόρτωσης wallet.dat: Το Πορτοφόλι απαιτεί μια νεότερη έκδοση του Bitcoin</translation> </message> <message> + <source>Error reading from database, shutting down.</source> + <translation>Σφάλμα ανάγνωσης από τη βάση δεδομένων, γίνεται τερματισμός.</translation> + </message> + <message> <source>Error: Unsupported argument -tor found, use -onion.</source> <translation>Σφάλμα: Μη συμβατή παράμετρος -tor. Χρησιμοποιήσε την παράμετρο -onion</translation> </message> @@ -2450,6 +2654,10 @@ <translation>Πληροφορία</translation> </message> <message> + <source>Initialization sanity check failed. Bitcoin Core is shutting down.</source> + <translation>Η εκκίνηση ελέγχου ορθότητας απέτυχε. Γίνεται τερματισμός του Bitcoin Core.</translation> + </message> + <message> <source>Invalid amount for -minrelaytxfee=<amount>: '%s'</source> <translation>Μη έγκυρο ποσό για την παράμετρο -paytxfee=<amount>: '%s'</translation> </message> @@ -2458,10 +2666,18 @@ <translation>Μη έγκυρο ποσό για την παράμετρο -paytxfee=<amount>: '%s'</translation> </message> <message> + <source>Node relay options:</source> + <translation>Επιλογές αναμετάδοσης κόμβου: </translation> + </message> + <message> <source>RPC SSL options: (see the Bitcoin Wiki for SSL setup instructions)</source> <translation>Ρυθμίσεις SSL: (ανατρέξτε στο Bitcoin Wiki για οδηγίες ρυθμίσεων SSL)</translation> </message> <message> + <source>RPC server options:</source> + <translation>Επιλογές διακομιστή RPC:</translation> + </message> + <message> <source>Send trace/debug info to console instead of debug.log file</source> <translation>Αποστολή πληροφοριών εντοπισμού σφαλμάτων στην κονσόλα αντί του αρχείου debug.log</translation> </message> @@ -2474,6 +2690,10 @@ <translation>Όρισε γλώσσα, για παράδειγμα "de_DE"(προεπιλογή:τοπικές ρυθμίσεις)</translation> </message> <message> + <source>Show all debugging options (usage: --help -help-debug)</source> + <translation>Προβολή όλων των επιλογών εντοπισμού σφαλμάτων (χρήση: --help -help-debug)</translation> + </message> + <message> <source>Show splash screen on startup (default: 1)</source> <translation>Εμφάνισε την οθόνη εκκίνησης κατά την εκκίνηση(προεπιλογή:1)</translation> </message> diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 8dfef68373..90a13feaca 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -1459,13 +1459,13 @@ <translation>Form</translation> </message> <message> - <location line="+53"/> - <location line="+372"/> + <location line="+59"/> + <location line="+386"/> <source>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</source> <translation>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</translation> </message> <message> - <location line="-133"/> + <location line="-139"/> <source>Watch-only:</source> <translation type="unfinished"></translation> </message> @@ -1500,12 +1500,12 @@ <translation>Mined balance that has not yet matured</translation> </message> <message> - <location line="-163"/> + <location line="-177"/> <source>Balances</source> <translation type="unfinished"></translation> </message> <message> - <location line="+147"/> + <location line="+161"/> <source>Total:</source> <translation>Total:</translation> </message> @@ -1544,12 +1544,6 @@ <source>Current total balance in watch-only addresses</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../overviewpage.cpp" line="+133"/> - <location line="+1"/> - <source>out of sync</source> - <translation>out of sync</translation> - </message> </context> <context> <name>PaymentServer</name> @@ -2648,14 +2642,14 @@ <context> <name>SendCoinsEntry</name> <message> - <location filename="../forms/sendcoinsentry.ui" line="+149"/> - <location line="+535"/> - <location line="+536"/> + <location filename="../forms/sendcoinsentry.ui" line="+155"/> + <location line="+539"/> + <location line="+533"/> <source>A&mount:</source> <translation>A&mount:</translation> </message> <message> - <location line="-1184"/> + <location line="-1185"/> <source>Pay &To:</source> <translation>Pay &To:</translation> </message> @@ -2675,12 +2669,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-40"/> + <location line="-46"/> <source>This is a normal payment.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+33"/> + <location line="+39"/> <source>The Bitcoin address to send the payment to</source> <translation type="unfinished"></translation> </message> @@ -2701,13 +2695,13 @@ </message> <message> <location line="+7"/> - <location line="+544"/> - <location line="+536"/> + <location line="+548"/> + <location line="+533"/> <source>Remove this entry</source> <translation type="unfinished"></translation> </message> <message> - <location line="-1020"/> + <location line="-1021"/> <source>The fee will be deducted from the amount being sent. The recipient will receive less bitcoins than you enter in the amount field. If multiple recipients are selected, the fee is split equally.</source> <translation type="unfinished"></translation> </message> @@ -2722,17 +2716,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+436"/> + <location line="+443"/> <source>This is an unauthenticated payment request.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+532"/> + <location line="+529"/> <source>This is an authenticated payment request.</source> <translation type="unfinished"></translation> </message> <message> - <location line="-1005"/> + <location line="-1009"/> <source>Enter a label for this address to add it to the list of used addresses</source> <translation type="unfinished"></translation> </message> @@ -2742,14 +2736,14 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+444"/> - <location line="+532"/> + <location line="+448"/> + <location line="+529"/> <source>Pay To:</source> <translation type="unfinished"></translation> </message> <message> - <location line="-498"/> - <location line="+536"/> + <location line="-495"/> + <location line="+533"/> <source>Memo:</source> <translation type="unfinished"></translation> </message> @@ -3589,27 +3583,27 @@ <translation>Options:</translation> </message> <message> - <location line="+34"/> + <location line="+35"/> <source>Specify data directory</source> <translation>Specify data directory</translation> </message> <message> - <location line="-93"/> + <location line="-94"/> <source>Connect to a node to retrieve peer addresses, and disconnect</source> <translation>Connect to a node to retrieve peer addresses, and disconnect</translation> </message> <message> - <location line="+96"/> + <location line="+97"/> <source>Specify your own public address</source> <translation>Specify your own public address</translation> </message> <message> - <location line="-116"/> + <location line="-117"/> <source>Accept command line and JSON-RPC commands</source> <translation>Accept command line and JSON-RPC commands</translation> </message> <message> - <location line="+94"/> + <location line="+95"/> <source>Run in the background as a daemon and accept commands</source> <translation>Run in the background as a daemon and accept commands</translation> </message> @@ -3619,7 +3613,7 @@ <translation>Use the test network</translation> </message> <message> - <location line="-135"/> + <location line="-136"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Accept connections from outside (default: 1 if no -proxy or -connect)</translation> </message> @@ -3839,7 +3833,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+17"/> + <location line="+18"/> <source>Set database cache size in megabytes (%d to %d, default: %d)</source> <translation type="unfinished"></translation> </message> @@ -3894,7 +3888,7 @@ <translation>You need to rebuild the database using -reindex to change -txindex</translation> </message> <message> - <location line="-98"/> + <location line="-99"/> <source>Imports blocks from external blk000??.dat file</source> <translation>Imports blocks from external blk000??.dat file</translation> </message> @@ -4194,6 +4188,11 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation type="unfinished"></translation> </message> <message> + <location line="+1"/> + <source>Rebuild block chain index from current blk000??.dat files on startup</source> + <translation type="unfinished"></translation> + </message> + <message> <location line="+7"/> <source>Send trace/debug info to console instead of debug.log file</source> <translation>Send trace/debug info to console instead of debug.log file</translation> @@ -4324,7 +4323,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>wallet.dat corrupt, salvage failed</translation> </message> <message> - <location line="-70"/> + <location line="-71"/> <source>Password for JSON-RPC connections</source> <translation>Password for JSON-RPC connections</translation> </message> @@ -4334,7 +4333,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Execute command when the best block changes (%s in cmd is replaced by block hash)</translation> </message> <message> - <location line="+257"/> + <location line="+258"/> <source>Upgrade wallet to latest format</source> <translation>Upgrade wallet to latest format</translation> </message> @@ -4354,7 +4353,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>This help message</translation> </message> <message> - <location line="-115"/> + <location line="-116"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Allow DNS lookups for -addnode, -seednode and -connect</translation> </message> @@ -4504,7 +4503,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation type="unfinished"></translation> </message> <message> - <location line="+8"/> + <location line="+9"/> <source>Relay and mine data carrier transactions (default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4584,7 +4583,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Unknown network specified in -onlynet: '%s'</translation> </message> <message> - <location line="-118"/> + <location line="-119"/> <source>Cannot resolve -bind address: '%s'</source> <translation>Cannot resolve -bind address: '%s'</translation> </message> @@ -4629,12 +4628,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Cannot write default address</translation> </message> <message> - <location line="+76"/> + <location line="+77"/> <source>Rescanning...</source> <translation>Rescanning...</translation> </message> <message> - <location line="-63"/> + <location line="-64"/> <source>Done loading</source> <translation>Done loading</translation> </message> diff --git a/src/qt/locale/bitcoin_eo.ts b/src/qt/locale/bitcoin_eo.ts index fbfac8fc10..007acbc495 100644 --- a/src/qt/locale/bitcoin_eo.ts +++ b/src/qt/locale/bitcoin_eo.ts @@ -949,11 +949,7 @@ <source>Your current total balance</source> <translation>via aktuala totala saldo</translation> </message> - <message> - <source>out of sync</source> - <translation>nesinkronigita</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> <message> diff --git a/src/qt/locale/bitcoin_es.ts b/src/qt/locale/bitcoin_es.ts index 2e1b7dd278..17ec4dca5d 100644 --- a/src/qt/locale/bitcoin_es.ts +++ b/src/qt/locale/bitcoin_es.ts @@ -1175,10 +1175,6 @@ <source>Current total balance in watch-only addresses</source> <translation>Saldo total en las direcciones watch-only</translation> </message> - <message> - <source>out of sync</source> - <translation>desincronizado</translation> - </message> </context> <context> <name>PaymentServer</name> diff --git a/src/qt/locale/bitcoin_es_CL.ts b/src/qt/locale/bitcoin_es_CL.ts index c3690480d8..f50aa49110 100644 --- a/src/qt/locale/bitcoin_es_CL.ts +++ b/src/qt/locale/bitcoin_es_CL.ts @@ -582,11 +582,7 @@ <source>Total:</source> <translation>Total:</translation> </message> - <message> - <source>out of sync</source> - <translation>desincronizado</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> <message> diff --git a/src/qt/locale/bitcoin_es_DO.ts b/src/qt/locale/bitcoin_es_DO.ts index c5b1e41886..d2cdf87a0e 100644 --- a/src/qt/locale/bitcoin_es_DO.ts +++ b/src/qt/locale/bitcoin_es_DO.ts @@ -953,11 +953,7 @@ <source>Your current total balance</source> <translation>Su balance actual total</translation> </message> - <message> - <source>out of sync</source> - <translation>desincronizado</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> <message> diff --git a/src/qt/locale/bitcoin_et.ts b/src/qt/locale/bitcoin_et.ts index 4a502f1a54..29a45960c9 100644 --- a/src/qt/locale/bitcoin_et.ts +++ b/src/qt/locale/bitcoin_et.ts @@ -755,11 +755,7 @@ <source>Recent transactions</source> <translation>Hiljutised tehingud</translation> </message> - <message> - <source>out of sync</source> - <translation>sünkimata</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> <message> diff --git a/src/qt/locale/bitcoin_fa.ts b/src/qt/locale/bitcoin_fa.ts index 0bed017614..33f43f0c2d 100644 --- a/src/qt/locale/bitcoin_fa.ts +++ b/src/qt/locale/bitcoin_fa.ts @@ -2,6 +2,10 @@ <context> <name>AddressBookPage</name> <message> + <source>Right-click to edit address or label</source> + <translation>برای تغییر آدرس و یا برچسب کلیک راست کنید.</translation> + </message> + <message> <source>Create a new address</source> <translation>ایجاد نشانی جدید</translation> </message> @@ -42,6 +46,22 @@ <translation>&حذف</translation> </message> <message> + <source>Choose the address to send coins to</source> + <translation>آدرس مورد نظر برای ارسال کوین ها را انتخاب کنید</translation> + </message> + <message> + <source>Choose the address to receive coins with</source> + <translation>آدرس موردنظر برای دریافت کوین ها را انتخاب کنید.</translation> + </message> + <message> + <source>Sending addresses</source> + <translation>آدرس های ارسال کننده</translation> + </message> + <message> + <source>Receiving addresses</source> + <translation>آدرس های دریافت کننده</translation> + </message> + <message> <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source> <translation>اینها نشانیهای بیتکوین شما برای ارسال وجود هستند. همیشه قبل از ارسال سکهها، نشانی دریافتکننده و مقدار ارسالی را بررسی کنید.</translation> </message> @@ -322,6 +342,10 @@ <source>Bitcoin Core</source> <translation> هسته Bitcoin </translation> </message> + <message> + <source>&About Bitcoin Core</source> + <translation>درباره هسته ی بیت کوین</translation> + </message> <message numerus="yes"> <source>%n active connection(s) to Bitcoin network</source> <translation><numerusform>%n ارتباط فعال با شبکهٔ بیتکوین</numerusform></translation> @@ -401,10 +425,38 @@ <context> <name>CoinControlDialog</name> <message> + <source>Coin Selection</source> + <translation>انتخاب سکه</translation> + </message> + <message> + <source>Quantity:</source> + <translation>تعداد:</translation> + </message> + <message> + <source>Bytes:</source> + <translation>بایت ها:</translation> + </message> + <message> <source>Amount:</source> <translation>مبلغ:</translation> </message> <message> + <source>Priority:</source> + <translation>اولویت:</translation> + </message> + <message> + <source>Fee:</source> + <translation>هزینه:</translation> + </message> + <message> + <source>After Fee:</source> + <translation>هزینه ی پسین:</translation> + </message> + <message> + <source>Change:</source> + <translation>پول خورد:</translation> + </message> + <message> <source>Amount</source> <translation>مبلغ</translation> </message> @@ -705,11 +757,7 @@ <source>Your current total balance</source> <translation>تراز کل فعلی شما</translation> </message> - <message> - <source>out of sync</source> - <translation>ناهمگام</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> <message> @@ -905,10 +953,34 @@ <translation>ارسال سکه</translation> </message> <message> + <source>Quantity:</source> + <translation>تعداد:</translation> + </message> + <message> + <source>Bytes:</source> + <translation>بایت ها:</translation> + </message> + <message> <source>Amount:</source> <translation>مبلغ:</translation> </message> <message> + <source>Priority:</source> + <translation>اولویت:</translation> + </message> + <message> + <source>Fee:</source> + <translation>هزینه:</translation> + </message> + <message> + <source>After Fee:</source> + <translation>هزینه ی پسین:</translation> + </message> + <message> + <source>Change:</source> + <translation>پول خورد:</translation> + </message> + <message> <source>Send to multiple recipients at once</source> <translation>ارسال به چند دریافتکنندهٔ بهطور همزمان</translation> </message> diff --git a/src/qt/locale/bitcoin_fa_IR.ts b/src/qt/locale/bitcoin_fa_IR.ts index 2dde67d3d5..da95f10474 100644 --- a/src/qt/locale/bitcoin_fa_IR.ts +++ b/src/qt/locale/bitcoin_fa_IR.ts @@ -390,11 +390,7 @@ <source>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</source> <translation>اطلاعات نمایش داده شده ممکن است روزآمد نباشد. wallet شما به صورت خودکار بعد از برقراری اتصال با شبکه bitcoin به روز می شود اما این فرایند هنوز تکمیل نشده است.</translation> </message> - <message> - <source>out of sync</source> - <translation>خارج از روزآمد سازی</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> </context> diff --git a/src/qt/locale/bitcoin_fi.ts b/src/qt/locale/bitcoin_fi.ts index 0ca66e8b26..db59ea1751 100644 --- a/src/qt/locale/bitcoin_fi.ts +++ b/src/qt/locale/bitcoin_fi.ts @@ -1137,10 +1137,6 @@ <source>Current total balance in watch-only addresses</source> <translation>Nykyinen tase seurantaosoitetteissa</translation> </message> - <message> - <source>out of sync</source> - <translation>Ei ajan tasalla</translation> - </message> </context> <context> <name>PaymentServer</name> diff --git a/src/qt/locale/bitcoin_fr.ts b/src/qt/locale/bitcoin_fr.ts index fe4288467b..67d920fd5e 100644 --- a/src/qt/locale/bitcoin_fr.ts +++ b/src/qt/locale/bitcoin_fr.ts @@ -1207,10 +1207,6 @@ <source>Current total balance in watch-only addresses</source> <translation>Solde total actuel dans des adresses juste-regarder</translation> </message> - <message> - <source>out of sync</source> - <translation>désynchronisé</translation> - </message> </context> <context> <name>PaymentServer</name> @@ -3273,6 +3269,10 @@ par exemple : alertnotify=echo %%s | mail -s "Alerte Bitcoin" admin@foo.com <translation>Tester aléatoirement 1 message du réseau sur <n></translation> </message> <message> + <source>Rebuild block chain index from current blk000??.dat files on startup</source> + <translation>Reconstruire au démarrage l'index de la chaîne de blocs à partir des fichiers blk000??.dat actuels</translation> + </message> + <message> <source>Send trace/debug info to console instead of debug.log file</source> <translation>Envoyer les informations de débogage/trace à la console au lieu du fichier debug.log</translation> </message> diff --git a/src/qt/locale/bitcoin_gl.ts b/src/qt/locale/bitcoin_gl.ts index 93ece390d1..2473260c84 100644 --- a/src/qt/locale/bitcoin_gl.ts +++ b/src/qt/locale/bitcoin_gl.ts @@ -869,11 +869,7 @@ <source>Your current total balance</source> <translation>O teu balance actual total</translation> </message> - <message> - <source>out of sync</source> - <translation>non sincronizado</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> <message> diff --git a/src/qt/locale/bitcoin_he.ts b/src/qt/locale/bitcoin_he.ts index 0a4b2f7847..66dd05fca7 100644 --- a/src/qt/locale/bitcoin_he.ts +++ b/src/qt/locale/bitcoin_he.ts @@ -1073,10 +1073,6 @@ <source>Current total balance in watch-only addresses</source> <translation>המאזן הכולל הנוכחי בכתובות לצפייה בלבד</translation> </message> - <message> - <source>out of sync</source> - <translation>לא בסנכרון</translation> - </message> </context> <context> <name>PaymentServer</name> diff --git a/src/qt/locale/bitcoin_hu.ts b/src/qt/locale/bitcoin_hu.ts index 0d9a57d3b4..c84d2c4e87 100644 --- a/src/qt/locale/bitcoin_hu.ts +++ b/src/qt/locale/bitcoin_hu.ts @@ -1001,11 +1001,7 @@ <source>Recent transactions</source> <translation>A legutóbbi tranzakciók</translation> </message> - <message> - <source>out of sync</source> - <translation>Nincs szinkronban.</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> <message> diff --git a/src/qt/locale/bitcoin_id_ID.ts b/src/qt/locale/bitcoin_id_ID.ts index 7bc7ed4607..dec30dafb3 100644 --- a/src/qt/locale/bitcoin_id_ID.ts +++ b/src/qt/locale/bitcoin_id_ID.ts @@ -1005,11 +1005,7 @@ <source>Your current total balance</source> <translation>Jumlah saldo Anda sekarang</translation> </message> - <message> - <source>out of sync</source> - <translation>tidak tersinkron</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> <message> diff --git a/src/qt/locale/bitcoin_it.ts b/src/qt/locale/bitcoin_it.ts index 2aa1e1da43..c81f458e39 100644 --- a/src/qt/locale/bitcoin_it.ts +++ b/src/qt/locale/bitcoin_it.ts @@ -168,6 +168,10 @@ <translation>Si è sicuri di voler cifrare il portamonete?</translation> </message> <message> + <source>Bitcoin Core will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> + <translation>Bitcoin Core si chiuderà per portare a termine il processo di cifratura. Si ricorda che la cifratura del portamonete non garantisce protezione totale contro i furti causati da infezioni malware.</translation> + </message> + <message> <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> <translation>IMPORTANTE: qualsiasi backup del file portamonete effettuato in precedenza dovrà essere sostituito con il file del portamonete cifrato appena generato. Per ragioni di sicurezza, i precedenti backup del file del portamonete non cifrato diventeranno inservibili non appena si inizierà ad utilizzare il nuovo portamonete cifrato.</translation> </message> @@ -184,6 +188,10 @@ <translation>Inserisci la nuova passphrase per il portamonete.<br/>Si consiglia di utilizzare <b>almeno dieci caratteri casuali</b> oppure <b>otto o più parole</b>.</translation> </message> <message> + <source>Enter the old passphrase and new passphrase to the wallet.</source> + <translation>Inserisci la vecchia e la nuova passphrase per il portamonete.</translation> + </message> + <message> <source>Wallet encryption failed</source> <translation>Cifratura del portamonete fallita</translation> </message> @@ -391,6 +399,10 @@ <translation>&Informazioni su Bitcoin Core</translation> </message> <message> + <source>Modify configuration options for Bitcoin Core</source> + <translation>Modifica opzioni di configurazione per Bitcoin Core</translation> + </message> + <message> <source>Show the list of used sending addresses and labels</source> <translation>Mostra la lista degli indirizzi di invio utilizzati</translation> </message> @@ -419,6 +431,10 @@ <translation>Nessuna fonte di blocchi disponibile...</translation> </message> <message numerus="yes"> + <source>Processed %n block(s) of transaction history.</source> + <translation><numerusform>Elaborato %n blocco dello storico transazioni.</numerusform><numerusform>Elaborati %n blocchi dello storico transazioni.</numerusform></translation> + </message> + <message numerus="yes"> <source>%n hour(s)</source> <translation><numerusform>%n ora</numerusform><numerusform>%n ore</numerusform></translation> </message> @@ -471,6 +487,36 @@ <translation>In aggiornamento...</translation> </message> <message> + <source>Date: %1 +</source> + <translation>Data: %1 +</translation> + </message> + <message> + <source>Amount: %1 +</source> + <translation>Quantità: %1 +</translation> + </message> + <message> + <source>Type: %1 +</source> + <translation>Tipo: %1 +</translation> + </message> + <message> + <source>Label: %1 +</source> + <translation>Etichetta: %1 +</translation> + </message> + <message> + <source>Address: %1 +</source> + <translation>Indirizzo: %1 +</translation> + </message> + <message> <source>Sent transaction</source> <translation>Transazione inviata</translation> </message> @@ -669,6 +715,18 @@ <translation>nessuno</translation> </message> <message> + <source>This label turns red if the transaction size is greater than 1000 bytes.</source> + <translation>Questa etichetta diventerà rossa se la dimensione della transazione supererà i 1000 byte.</translation> + </message> + <message> + <source>This label turns red if the priority is smaller than "medium".</source> + <translation>Questa etichetta diventerà rossa se la priorità sarà inferiore a "media".</translation> + </message> + <message> + <source>This label turns red if any recipient receives an amount smaller than %1.</source> + <translation>Questa etichetta diventerà rossa se uno qualsiasi dei destinatari riceverà un importo inferiore a %1.</translation> + </message> + <message> <source>Can vary +/- %1 satoshi(s) per input.</source> <translation>Può variare di +/- %1 satoshi per input.</translation> </message> @@ -911,6 +969,14 @@ <translation>Indirizzo IP del proxy (ad es. IPv4: 127.0.0.1 / IPv6: ::1)</translation> </message> <message> + <source>Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</source> + <translation>Riduci ad icona invece di uscire dall'applicazione quando la finestra viene chiusa. Attivando questa opzione l'applicazione terminerà solo dopo aver selezionato Esci dal menu File.</translation> + </message> + <message> + <source>The user interface language can be set here. This setting will take effect after restarting Bitcoin Core.</source> + <translation>La lingua dell'interfaccia utente può essere impostata qui. L'applicazione delle modifiche avrà effetto dopo il riavvio di Bitcoin Core.</translation> + </message> + <message> <source>Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</source> <translation>URL di terze parti (ad es. un block explorer) che appaiono nella tabella delle transazioni come voci nel menu contestuale. "%s" nell'URL è sostituito dall'hash della transazione. Per specificare più URL separarli con una barra verticale "|".</translation> @@ -936,6 +1002,14 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>Rete</translation> </message> <message> + <source>Automatically start Bitcoin Core after logging in to the system.</source> + <translation>Avvia automaticamente Bitcoin Core una volta effettuato l'accesso al sistema.</translation> + </message> + <message> + <source>&Start Bitcoin Core on system login</source> + <translation>&Avvia Bitcoin Core all'accesso al sistema</translation> + </message> + <message> <source>(0 = auto, <0 = leave that many cores free)</source> <translation>(0 = automatico, <0 = lascia questo numero di core liberi)</translation> </message> @@ -1048,6 +1122,10 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>È necessario un riavvio del client per applicare le modifiche.</translation> </message> <message> + <source>Client will be shut down. Do you want to proceed?</source> + <translation>Il client sarà arrestato. Si desidera procedere?</translation> + </message> + <message> <source>This change would require a client restart.</source> <translation>Questa modifica richiede un riavvio del client.</translation> </message> @@ -1130,10 +1208,6 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <source>Current total balance in watch-only addresses</source> <translation>Saldo corrente totale negli indirizzi di sola lettura</translation> </message> - <message> - <source>out of sync</source> - <translation>non sincronizzato</translation> - </message> </context> <context> <name>PaymentServer</name> @@ -1186,10 +1260,18 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>Impossibile leggere il file della richiesta di pagamento! Il file della richiesta di pagamento potrebbe non essere valido.</translation> </message> <message> + <source>Payment request expired.</source> + <translation>Richiesta di pagamento scaduta.</translation> + </message> + <message> <source>Unverified payment requests to custom payment scripts are unsupported.</source> <translation>Le richieste di pagamento non verificate verso script di pagamento personalizzati non sono supportate.</translation> </message> <message> + <source>Invalid payment request.</source> + <translation>Richiesta di pagamento non valida.</translation> + </message> + <message> <source>Refund from %1</source> <translation>Rimborso da %1</translation> </message> @@ -1229,6 +1311,10 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>User Agent</translation> </message> <message> + <source>Node/Service</source> + <translation>Nodo/Servizio</translation> + </message> + <message> <source>Ping Time</source> <translation>Tempo di ping</translation> </message> @@ -1350,6 +1436,10 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>Numero attuale di blocchi</translation> </message> <message> + <source>Open the Bitcoin Core debug log file from the current data directory. This can take a few seconds for large log files.</source> + <translation>Apre il file log di debug di Bitcoin Core dalla cartella dati attuale. Questa azione può richiedere alcuni secondi per file log di grandi dimensioni.</translation> + </message> + <message> <source>Received</source> <translation>Ricevuto</translation> </message> @@ -1418,6 +1508,10 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>Tempo di Ping</translation> </message> <message> + <source>Time Offset</source> + <translation>Scarto Temporale</translation> + </message> + <message> <source>Last block time</source> <translation>Ora del blocco più recente</translation> </message> @@ -1462,6 +1556,10 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>Cancella console</translation> </message> <message> + <source>Welcome to the Bitcoin Core RPC console.</source> + <translation>Benvenuto nella console RPC di Bitcoin Core.</translation> + </message> + <message> <source>Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen.</source> <translation>Usa le frecce direzionali per scorrere la cronologia, e <b>Ctrl-L</b> per cancellarla.</translation> </message> @@ -1758,6 +1856,10 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>Se la commissione personalizzata è impostata su 1000 satoshi e la transazione è di soli 250 byte, allora "per kilobyte" paga solo 250 satoshi di commissione, mentre "somma almeno" paga 1000 satoshi. Per transazioni più grandi di un kilobyte, entrambe le opzioni pagano al kilobyte.</translation> </message> <message> + <source>Hide</source> + <translation>Nascondi</translation> + </message> + <message> <source>total at least</source> <translation>somma almeno</translation> </message> @@ -1898,10 +2000,26 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>La transazione è stata respinta! Questo può accadere se alcuni bitcoin nel tuo portamonete sono già stati spesi, come nel caso in cui tu avessi utilizzato una copia del file wallet.dat per spendere bitcoin e questi non fossero stati considerati come spesi dal portamonete corrente.</translation> </message> <message> + <source>A fee higher than %1 is considered an absurdly high fee.</source> + <translation>Una commissione maggiore di %1 è considerata irragionevolmente elevata.</translation> + </message> + <message> + <source>Payment request expired.</source> + <translation>Richiesta di pagamento scaduta.</translation> + </message> + <message> <source>Pay only the minimum fee of %1</source> <translation>Paga solamente la commissione minima di %1</translation> </message> <message> + <source>The recipient address is not valid. Please recheck.</source> + <translation>L'indirizzo del beneficiario non è valido. Si prega di ricontrollare.</translation> + </message> + <message> + <source>Duplicate address found: addresses should only be used once each.</source> + <translation>Rilevato un indirizzo duplicato Ciascun indirizzo dovrebbe essere utilizzato una sola volta.</translation> + </message> + <message> <source>Warning: Invalid Bitcoin address</source> <translation>Attenzione: Indirizzo Bitcoin non valido</translation> </message> @@ -1973,10 +2091,26 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>Rimuovi questa voce</translation> </message> <message> + <source>The fee will be deducted from the amount being sent. The recipient will receive less bitcoins than you enter in the amount field. If multiple recipients are selected, the fee is split equally.</source> + <translation>La commissione sarà sottratta dall'importo che si sta inviando. Il beneficiario riceverà un totale di bitcoin inferiore al valore digitato. Nel caso in cui siano stati selezionati più beneficiari la commissione sarà suddivisa in parti uguali.</translation> + </message> + <message> + <source>S&ubtract fee from amount</source> + <translation>S&ottrae la commissione dall'importo</translation> + </message> + <message> <source>Message:</source> <translation>Messaggio:</translation> </message> <message> + <source>This is an unauthenticated payment request.</source> + <translation>Questa è una richiesta di pagamento non autenticata.</translation> + </message> + <message> + <source>This is an authenticated payment request.</source> + <translation>Questa è una richiesta di pagamento autenticata.</translation> + </message> + <message> <source>Enter a label for this address to add it to the list of used addresses</source> <translation>Inserisci un'etichetta per questo indirizzo per aggiungerlo alla lista degli indirizzi utilizzati</translation> </message> @@ -2015,6 +2149,10 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>&Firma Messaggio</translation> </message> <message> + <source>You can sign messages/agreements with your addresses to prove you can receive bitcoins sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to.</source> + <translation>È possibile firmare messaggi/accordi con i propri indirizzi in modo da dimostrare di poter ricevere bitcoin attraverso di essi. Si consiglia di prestare attenzione a non firmare dichiarazioni vaghe o casuali, attacchi di phishing potrebbero cercare di indurre ad apporre la firma su di esse. Si raccomanda di firmare esclusivamente dichiarazioni completamente dettagliate e delle quali si condivide in pieno il contenuto.</translation> + </message> + <message> <source>The Bitcoin address to sign the message with</source> <translation>L'indirizzo Bitcoin da utilizzare per firmare il messaggio</translation> </message> @@ -2067,6 +2205,10 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>&Verifica Messaggio</translation> </message> <message> + <source>Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction!</source> + <translation>Per verificare il messaggio inserire l'indirizzo del firmatario, il messaggio e la firma nei campi sottostanti, assicurandosi di copiare esattamente anche ritorni a capo, spazi, tabulazioni, etc.. Si raccomanda di non lasciarsi fuorviare dalla firma a leggere più di quanto non sia riportato nel testo del messaggio stesso, in modo da evitare di cadere vittima di attacchi di tipo man-in-the-middle. Si ricorda che la verifica della firma dimostra soltanto che il firmatario può ricevere pagamenti con l'indirizzo corrispondente, non prova l'invio di alcuna transazione.</translation> + </message> + <message> <source>The Bitcoin address the message was signed with</source> <translation>L'indirizzo Bitcoin con cui è stato contrassegnato il messaggio</translation> </message> @@ -2418,6 +2560,10 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>Indica se un indirizzo di sola lettura sia o meno coinvolto in questa transazione.</translation> </message> <message> + <source>User-defined intent/purpose of the transaction.</source> + <translation>Intento/scopo della transazione definito dall'utente.</translation> + </message> + <message> <source>Amount removed from or added to balance.</source> <translation>Importo rimosso o aggiunto al saldo.</translation> </message> @@ -2660,6 +2806,10 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>Associa all'indirizzo indicato e resta permanentemente in ascolto su di esso. Usa la notazione [host]:porta per l'IPv6</translation> </message> <message> + <source>Continuously rate-limit free transactions to <n>*1000 bytes per minute (default: %u)</source> + <translation>Limita la quantità di transazioni gratuite ad <n>*1000 byte al minuto (predefinito: %u)</translation> + </message> + <message> <source>Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup</source> <translation>Elimina tutte le transazioni dal portamonete e recupera solo quelle che fanno parte della blockchain attraverso il comando -rescan all'avvio.</translation> </message> @@ -2680,6 +2830,14 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>In questa modalità -genproclimit determina quanti blocchi saranno generati immediatamente.</translation> </message> <message> + <source>Maximum total fees to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)</source> + <translation>Commissioni massime totali da includere in una singola transazione dal portamonete. Un'impostazione troppo bassa potrebbe provocare l'annullamento di transazioni di grosse dimensioni (predefinito: %s)</translation> + </message> + <message> + <source>Reduce storage requirements by pruning (deleting) old blocks. This mode disables wallet support and is incompatible with -txindex. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> + <translation>Riduce i requisiti di spazio di archiviazione attraverso la rimozione dei vecchi blocchi (pruning). Questa modalità disabilita le funzionalità di portamonete ed è incompatibile con l'opzione -txindex. Attenzione: il ripristinando questa opzione l'intera blockchain dovrà essere riscaricata. (predefinito: 0 = disabilita il pruning, >%u = dimensione desiderata in MiB per i file dei blocchi)</translation> + </message> + <message> <source>Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)</source> <translation>Imposta il numero di thread per la verifica degli script (da %u a %d, 0 = automatico, <0 = lascia questo numero di core liberi, predefinito: %d)</translation> </message> @@ -2808,6 +2966,14 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>Connessione ai soli nodi appartenenti alla rete <net> (ipv4, ipv6 o Tor)</translation> </message> <message> + <source>Prune cannot be configured with a negative value.</source> + <translation>La modalità prune non può essere configurata con un valore negativo.</translation> + </message> + <message> + <source>Prune mode is incompatible with -txindex.</source> + <translation>La modalità prune è incompatibile con l'opzione -txindex.</translation> + </message> + <message> <source>Set database cache size in megabytes (%d to %d, default: %d)</source> <translation>Imposta la dimensione della cache del database in megabyte (%d a %d, predefinito: %d)</translation> </message> @@ -2844,6 +3010,10 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>Opzioni portamonete:</translation> </message> <message> + <source>Warning: This version is obsolete; upgrade required!</source> + <translation>Attenzione: questa versione è obsoleta. Aggiornamento necessario!</translation> + </message> + <message> <source>You need to rebuild the database using -reindex to change -txindex</source> <translation>È necessario ricostruire il database usando -reindex per cambiare -txindex</translation> </message> @@ -2872,6 +3042,10 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>Crea nuovi file con i permessi di default del sistema, invece che con umask 077 (ha effetto solo con funzionalità di portamonete disabilitate)</translation> </message> <message> + <source>Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)</source> + <translation>Scopre i propri indirizzi IP (predefinito: 1 se in ascolto ed -externalip o -proxy non sono specificati)</translation> + </message> + <message> <source>Error: Listening for incoming connections failed (listen returned error %s)</source> <translation>Errore: attesa per connessioni in arrivo fallita (errore riportato %s)</translation> </message> @@ -2904,10 +3078,22 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>Dimensione massima dei dati in transazioni di trasporto dati che saranno trasmesse ed incluse nei blocchi (predefinito: %u)</translation> </message> <message> + <source>Prune configured below the minimum of %d MB. Please use a higher number.</source> + <translation>La modalità prune è configurata al di sotto del minimo di %d MB. Si prega di utilizzare un valore più elevato.</translation> + </message> + <message> <source>Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect)</source> <translation>Ottiene gli indirizzi dei peer attraverso interrogazioni DNS, in caso di scarsa disponibilità (predefinito: 1 a meno che -connect non sia specificato)</translation> </message> <message> + <source>Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)</source> + <translation>Randomizza le credenziali per ogni connessione proxy. Permette la Tor stream isolation (predefinito: %u)</translation> + </message> + <message> + <source>Require high priority for relaying free or low-fee transactions (default: %u)</source> + <translation>Richiedi alta priorità per la trasmissione di transazioni a zero o basse commissioni (predefinito: %u)</translation> + </message> + <message> <source>Set maximum size of high-priority/low-fee transactions in bytes (default: %d)</source> <translation>Imposta la dimensione massima in byte delle transazioni ad alta-priorità/basse-commissioni (predefinito: %d)</translation> </message> @@ -2916,6 +3102,10 @@ Per specificare più URL separarli con una barra verticale "|".</translation> <translation>Specifica il numero di thread per la generazione di bitcoin, se abilitata (-1 = tutti i core, predefinito: %d)</translation> </message> <message> + <source>The transaction amount is too small to send after the fee has been deducted</source> + <translation>L'importo della transazione risulta troppo basso per l'invio una volta dedotte le commissioni.</translation> + </message> + <message> <source>This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit <https://www.openssl.org/> and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard.</source> <translation>Questo prodotto include software sviluppato dal progetto OpenSSL per l'uso del Toolkit OpenSSL <https://www.openssl.org/>, software crittografico scritto da Eric Young e software UPnP scritto da Thomas Bernard.</translation> </message> @@ -2955,6 +3145,10 @@ Si raccomanda inoltre di configurare alertnotify in modo da ricevere notifiche d <translation>I peer inclusi in whitelist non possono subire ban per DoS e le loro transazioni saranno sempre trasmesse, anche nel caso in cui si trovino già nel mempool. Ciò è utile ad es. per i gateway</translation> </message> <message> + <source>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source> + <translation>Per ritornare alla modalità unpruned sarà necessario ricostruire il database utilizzando l'opzione -reindex. L'intera blockchain sarà riscaricata.</translation> + </message> + <message> <source>(default: %u)</source> <translation>(default: %u)</translation> </message> @@ -2963,6 +3157,18 @@ Si raccomanda inoltre di configurare alertnotify in modo da ricevere notifiche d <translation>Accetta richieste REST pubbliche (predefinito: %u)</translation> </message> <message> + <source>Activating best chain...</source> + <translation>Attivazione della blockchain migliore...</translation> + </message> + <message> + <source>Allow self signed root certificates (default: 0)</source> + <translation>Permette certificati radice auto-firmati (predefinito: 0)</translation> + </message> + <message> + <source>Can't run with a wallet in prune mode.</source> + <translation>Impossibile operare con un portamonete in modalità prune.</translation> + </message> + <message> <source>Cannot resolve -whitebind address: '%s'</source> <translation>Impossibile risolvere indirizzo -whitebind: '%s'</translation> </message> @@ -3095,6 +3301,10 @@ Si raccomanda inoltre di configurare alertnotify in modo da ricevere notifiche d <translation>Avvia ridotto a icona</translation> </message> <message> + <source>The transaction amount is too small to pay the fee</source> + <translation>L'importo della transazione è troppo basso per pagare la commissione</translation> + </message> + <message> <source>This is experimental software.</source> <translation>Questo è un software sperimentale.</translation> </message> @@ -3115,6 +3325,10 @@ Si raccomanda inoltre di configurare alertnotify in modo da ricevere notifiche d <translation>Transazione troppo grande</translation> </message> <message> + <source>UI Options:</source> + <translation>Opzioni Interfaccia Utente:</translation> + </message> + <message> <source>Unable to bind to %s on this computer (bind returned error %s)</source> <translation>Impossibile associarsi a %s su questo computer (l'associazione ha restituito l'errore %s)</translation> </message> @@ -3279,6 +3493,10 @@ Si raccomanda inoltre di configurare alertnotify in modo da ricevere notifiche d <translation>Mantiene al massimo <n> connessioni verso i peer (predefinito: %u)</translation> </message> <message> + <source>Make the wallet broadcast transactions</source> + <translation>Configura il portamonete per la trasmissione di transazioni</translation> + </message> + <message> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)</source> <translation>Buffer di ricezione massimo per connessione, <n>*1000 byte (predefinito: %u)</translation> </message> diff --git a/src/qt/locale/bitcoin_ja.ts b/src/qt/locale/bitcoin_ja.ts index 329141edff..376d36bed0 100644 --- a/src/qt/locale/bitcoin_ja.ts +++ b/src/qt/locale/bitcoin_ja.ts @@ -168,6 +168,10 @@ <translation>本当にウォレットを暗号化しますか?</translation> </message> <message> + <source>Bitcoin Core will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> + <translation>暗号化処理を完了させるため Bitcoin Core をいますぐ終了します。ウォレットの暗号化では、コンピュータに感染したマルウェアなどによるビットコインの盗難から完全に守ることはできないことにご注意ください。</translation> + </message> + <message> <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> <translation>重要: 過去のウォレット ファイルのバックアップは、暗号化された新しいウォレット ファイルに取り替える必要があります。セキュリティ上の理由により、暗号化された新しいウォレットを使い始めると、暗号化されていないウォレット ファイルのバックアップはすぐに使えなくなります。</translation> </message> @@ -184,6 +188,10 @@ <translation>ウォレットの新しいパスフレーズを入力してください。<br/><b>10文字以上のランダムな文字</b>で構成されたものか、<b>8単語以上の単語</b>で構成されたパスフレーズを使用してください。</translation> </message> <message> + <source>Enter the old passphrase and new passphrase to the wallet.</source> + <translation>ウォレットの古いパスフレーズおよび新しいパスフレーズを入力してください。</translation> + </message> + <message> <source>Wallet encryption failed</source> <translation>ウォレットの暗号化に失敗しました</translation> </message> @@ -391,6 +399,10 @@ <translation>ビットコインコアについて (&A)</translation> </message> <message> + <source>Modify configuration options for Bitcoin Core</source> + <translation>Bitcoin Core の設定を編集する</translation> + </message> + <message> <source>Show the list of used sending addresses and labels</source> <translation>使用済みの送金用アドレスとラベルの一覧を表示する</translation> </message> @@ -419,6 +431,10 @@ <translation>利用可能なブロックがありません...</translation> </message> <message numerus="yes"> + <source>Processed %n block(s) of transaction history.</source> + <translation><numerusform>トランザクション履歴の %n ブロックを処理しました。</numerusform></translation> + </message> + <message numerus="yes"> <source>%n hour(s)</source> <translation><numerusform>%n 時間</numerusform></translation> </message> @@ -471,6 +487,36 @@ <translation>追跡中...</translation> </message> <message> + <source>Date: %1 +</source> + <translation>日付: %1 +</translation> + </message> + <message> + <source>Amount: %1 +</source> + <translation>総額: %1 +</translation> + </message> + <message> + <source>Type: %1 +</source> + <translation>タイプ: %1 +</translation> + </message> + <message> + <source>Label: %1 +</source> + <translation>ラベル: %1 +</translation> + </message> + <message> + <source>Address: %1 +</source> + <translation>アドレス: %1 +</translation> + </message> + <message> <source>Sent transaction</source> <translation>送金取引</translation> </message> @@ -669,6 +715,18 @@ <translation>なし</translation> </message> <message> + <source>This label turns red if the transaction size is greater than 1000 bytes.</source> + <translation>トランザクションのサイズが1000バイトを超える場合にはこのラベルは赤色になります。</translation> + </message> + <message> + <source>This label turns red if the priority is smaller than "medium".</source> + <translation>優先度が「中」未満の場合、このラベルは赤色になります。</translation> + </message> + <message> + <source>This label turns red if any recipient receives an amount smaller than %1.</source> + <translation>受取人のうち誰かの受取額が %1 未満の場合にこのラベルは赤色になります。</translation> + </message> + <message> <source>Can vary +/- %1 satoshi(s) per input.</source> <translation>ひとつの入力につき %1 satoshi 前後ずれることがあります。</translation> </message> @@ -919,6 +977,14 @@ <translation>プロキシのIPアドレス (例えば IPv4: 127.0.0.1 / IPv6: ::1)</translation> </message> <message> + <source>Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</source> + <translation>ウィンドウを閉じる際にアプリケーションを終了するのではなく、最小化します。このオプションが有効化された場合、メニューから終了を選択した場合にのみアプリケーションは閉じられます。</translation> + </message> + <message> + <source>The user interface language can be set here. This setting will take effect after restarting Bitcoin Core.</source> + <translation>ユーザ・インタフェイス言語はここで設定できます。この設定はBitcoin Coreの再起動後に有効となります。</translation> + </message> + <message> <source>Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</source> <translation>トランザクションタブのコンテキストメニュー項目に表示する、サードパーティURL (例えばブロックエクスプローラ)。URL中の%sはトランザクションのハッシュ値に置き換えられます。垂直バー | で区切ることで、複数のURLを指定できます。</translation> </message> @@ -943,6 +1009,14 @@ <translation>ネットワーク (&N)</translation> </message> <message> + <source>Automatically start Bitcoin Core after logging in to the system.</source> + <translation>システムにログインした際、自動的にBitcoin Coreを起動する。</translation> + </message> + <message> + <source>&Start Bitcoin Core on system login</source> + <translation>システムへログインした際にBitcoin Coreを起動する (&S)</translation> + </message> + <message> <source>(0 = auto, <0 = leave that many cores free)</source> <translation>(0 = 自動、0以上 = 指定した数のコアをフリーにする)</translation> </message> @@ -1055,6 +1129,10 @@ <translation>変更を有効化するにはクライアントを再起動する必要があります。</translation> </message> <message> + <source>Client will be shut down. Do you want to proceed?</source> + <translation>クライアントを終了します。続行してもよろしいですか?</translation> + </message> + <message> <source>This change would require a client restart.</source> <translation>この変更はクライアントの再起動が必要です。</translation> </message> @@ -1137,10 +1215,6 @@ <source>Current total balance in watch-only addresses</source> <translation>監視限定アドレス内の現在の全残高</translation> </message> - <message> - <source>out of sync</source> - <translation>同期していない</translation> - </message> </context> <context> <name>PaymentServer</name> @@ -1193,10 +1267,18 @@ <translation>支払いリクエストファイルを読み込めませんでした!無効な支払いリクエストファイルにより引き起こされた可能性があります。</translation> </message> <message> + <source>Payment request expired.</source> + <translation>支払いリクエストの期限が切れました。</translation> + </message> + <message> <source>Unverified payment requests to custom payment scripts are unsupported.</source> <translation>カスタム支払いスクリプトに対する、検証されていない支払いリクエストはサポートされていません。</translation> </message> <message> + <source>Invalid payment request.</source> + <translation>無効な支払いリクエスト。</translation> + </message> + <message> <source>Refund from %1</source> <translation>%1 からの返金</translation> </message> @@ -1236,6 +1318,10 @@ <translation>ユーザエージェント</translation> </message> <message> + <source>Node/Service</source> + <translation>ノード・サービス</translation> + </message> + <message> <source>Ping Time</source> <translation>Ping時間</translation> </message> @@ -1357,6 +1443,10 @@ <translation>現在のブロック数</translation> </message> <message> + <source>Open the Bitcoin Core debug log file from the current data directory. This can take a few seconds for large log files.</source> + <translation>現在のデータディレクトリからBitcoin Coreのデバッグ用ログファイルを開きます。ログファイルが巨大な場合、数秒かかることがあります。</translation> + </message> + <message> <source>Received</source> <translation>受取</translation> </message> @@ -1425,6 +1515,10 @@ <translation>Ping時間</translation> </message> <message> + <source>Time Offset</source> + <translation>時間オフセット</translation> + </message> + <message> <source>Last block time</source> <translation>最終ブロックの日時</translation> </message> @@ -1469,6 +1563,10 @@ <translation>コンソールをクリア</translation> </message> <message> + <source>Welcome to the Bitcoin Core RPC console.</source> + <translation>Bitcoin CoreのRPCコンソールへようこそ。</translation> + </message> + <message> <source>Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen.</source> <translation>上下の矢印で履歴をたどれます。 <b>Ctrl-L</b> でスクリーンを消去できます。</translation> </message> @@ -1765,6 +1863,10 @@ <translation>カスタム手数料が1000satoshiに設定されている場合、トランザクションサイズが250バイトとすると、「1キロバイトあたり手数料」では250satoshiの手数料のみを支払いますが、「最小手数料」では1000satoshiを支払います。1キロバイトを超えるトランザクションの場合には、どちらの方法を選択したとしても1キロバイトあたりで支払われます。</translation> </message> <message> + <source>Hide</source> + <translation>隠す</translation> + </message> + <message> <source>total at least</source> <translation>最小手数料</translation> </message> @@ -1905,10 +2007,30 @@ <translation>トランザクションは拒否されました。wallet.dat のコピーを使い、そしてコピーしたウォレットからコインを使用したことがマークされなかったときなど、ウォレットのいくつかのコインがすでに使用されている場合に、このエラーは起こるかもしれません。</translation> </message> <message> + <source>A fee higher than %1 is considered an absurdly high fee.</source> + <translation>%1 よりも高い手数料の場合、手数料が高すぎると判断されます。</translation> + </message> + <message> + <source>Payment request expired.</source> + <translation>支払いリクエストの期限が切れました。</translation> + </message> + <message numerus="yes"> + <source>Estimated to begin confirmation within %n block(s).</source> + <translation><numerusform>%n ブロック以内に検証が開始されると予想されます。</numerusform></translation> + </message> + <message> <source>Pay only the minimum fee of %1</source> <translation>最小手数料 %1 のみを支払う</translation> </message> <message> + <source>The recipient address is not valid. Please recheck.</source> + <translation>受取アドレスが不正です。再チェックしてください。</translation> + </message> + <message> + <source>Duplicate address found: addresses should only be used once each.</source> + <translation>重複したアドレスが見つかりました: アドレスはそれぞれ一度のみ使用することができます。</translation> + </message> + <message> <source>Warning: Invalid Bitcoin address</source> <translation>警告:無効なBitcoinアドレスです</translation> </message> @@ -1980,10 +2102,26 @@ <translation>この項目を削除する</translation> </message> <message> + <source>The fee will be deducted from the amount being sent. The recipient will receive less bitcoins than you enter in the amount field. If multiple recipients are selected, the fee is split equally.</source> + <translation>送金する金額から手数料が差し引かれます。受取人は数量フィールドで指定した量よりも少ないビットコインを受け取ります。受取人が複数いる場合には、手数料は均等割されます。</translation> + </message> + <message> + <source>S&ubtract fee from amount</source> + <translation>送金額から手数料を差し引く (&U)</translation> + </message> + <message> <source>Message:</source> <translation>メッセージ:</translation> </message> <message> + <source>This is an unauthenticated payment request.</source> + <translation>これは未認証の支払いリクエストです。</translation> + </message> + <message> + <source>This is an authenticated payment request.</source> + <translation>これは認証済みの支払いリクエストです。</translation> + </message> + <message> <source>Enter a label for this address to add it to the list of used addresses</source> <translation>このアドレスに対するラベルを入力することで、使用済みアドレスの一覧に追加することができます</translation> </message> @@ -2022,6 +2160,10 @@ <translation>メッセージの署名 (&S)</translation> </message> <message> + <source>You can sign messages/agreements with your addresses to prove you can receive bitcoins sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to.</source> + <translation>あなたの所有しているアドレスによりメッセージや合意書に署名をすることで、それらアドレスに対して送られたビットコインを受け取ることができることを証明できます。フィッシング攻撃により不正にあなたの識別情報を署名させられてしまうことを防ぐために、不明確なものやランダムなものに対して署名しないよう注意してください。合意することが可能な、よく詳細の記された文言にのみ署名するようにしてください。</translation> + </message> + <message> <source>The Bitcoin address to sign the message with</source> <translation>メッセージを署名するBitcoinアドレス</translation> </message> @@ -2074,6 +2216,10 @@ <translation>メッセージの検証 (&V)</translation> </message> <message> + <source>Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction!</source> + <translation>受取人のアドレスとメッセージ(改行やスペース、タブなども完全に一致するよう注意してください)および署名を以下に入力し、メッセージの署名を検証してください。中間者攻撃により騙されるのを防ぐため、署名対象のメッセージに書かれていること以上の意味を署名から読み取ろうとしないよう注意してください。これは署名作成者がこのアドレスで受け取ったことを証明するだけであり、トランザクションの送信権限を証明するものではないことに注意してください!</translation> + </message> + <message> <source>The Bitcoin address the message was signed with</source> <translation>メッセージの署名に使われたBitcoinアドレス</translation> </message> @@ -2425,6 +2571,10 @@ <translation>監視限定アドレスがこのトランザクションに含まれているかどうか</translation> </message> <message> + <source>User-defined intent/purpose of the transaction.</source> + <translation>ユーザ定義のトランザクションの意図や目的。</translation> + </message> + <message> <source>Amount removed from or added to balance.</source> <translation>残高に追加または削除された総額。</translation> </message> @@ -2667,6 +2817,10 @@ <translation>指定のアドレスへバインドし、その上で常にリスンします。IPv6 は [ホスト名]:ポート番号 と表記します</translation> </message> <message> + <source>Continuously rate-limit free transactions to <n>*1000 bytes per minute (default: %u)</source> + <translation>継続的に無料トランザクションのレートを一分間に<n>*1000バイトに制限する (規定値: %u)</translation> + </message> + <message> <source>Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup</source> <translation>ウォレットの全トランザクションを削除し、これらを-rescanオプションを用いることで起動時にブロックチェインのデータのみからリカバリします。</translation> </message> @@ -2687,6 +2841,14 @@ <translation>このモードでは -genproclimit は何個のブロックをただちに生成するのか制御します。</translation> </message> <message> + <source>Maximum total fees to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)</source> + <translation>ひとつのウォレットトランザクションで使用する合計手数料の最大値。低すぎる値を指定すると巨大なトランザクションの作成ができなくなります (規定値: %s)</translation> + </message> + <message> + <source>Reduce storage requirements by pruning (deleting) old blocks. This mode disables wallet support and is incompatible with -txindex. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> + <translation>古いブロックを剪定する(削除する)ことで記憶容量の必要量を削減する。このモードを有効にするとウォレット機能のサポートは無効になり、-txindexとも互換性がなくなります。警告: この設定の再有効化には全ブロックチェインの再ダウンロードが必要となります。(規定値: 0 = ブロックの剪定無効、>%u = ブロックファイルに使用するMiB単位の目標サイズ)</translation> + </message> + <message> <source>Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)</source> <translation>スクリプト検証スレッドを設定 (%uから%dの間, 0 = 自動, <0 = たくさんのコアを自由にしておく, 初期値: %d)</translation> </message> @@ -2816,6 +2978,14 @@ <translation><net> (ipv4, ipv6 または onion) ネットワーク内のノードだけに接続する</translation> </message> <message> + <source>Prune cannot be configured with a negative value.</source> + <translation>剪定値は負の値に設定できません。</translation> + </message> + <message> + <source>Prune mode is incompatible with -txindex.</source> + <translation>剪定モードは-txindexと互換性がありません。</translation> + </message> + <message> <source>Set database cache size in megabytes (%d to %d, default: %d)</source> <translation>データベースのキャッシュサイズをメガバイトで設定 (%dから%d。初期値: %d)</translation> </message> @@ -2852,6 +3022,10 @@ <translation>ウォレットオプション:</translation> </message> <message> + <source>Warning: This version is obsolete; upgrade required!</source> + <translation>警告: このバージョンはサポートされません。アップグレードが必要です!</translation> + </message> + <message> <source>You need to rebuild the database using -reindex to change -txindex</source> <translation>-txindex を変更するには -reindex を使用してデータベースを再構築する必要があります</translation> </message> @@ -2884,6 +3058,10 @@ <translation>umask 077 ではなく、システムのデフォルトパーミッションで新規ファイルを作成する (ウォレット機能が無効化されていた場合にのみ有効)</translation> </message> <message> + <source>Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)</source> + <translation>自分のIPアドレスを解決する (規定値: リッスンをしており、-externalipまたは-proxyオプションが指定されていない場合は1)</translation> + </message> + <message> <source>Error: Listening for incoming connections failed (listen returned error %s)</source> <translation>エラー: 内向きの接続をリッスンするのに失敗しました (エラー %s が返却されました)</translation> </message> @@ -2916,10 +3094,22 @@ <translation>中継および採掘を行う際の、データ運送トランザクションの中のデータの最大サイズ (初期値: %u)</translation> </message> <message> + <source>Prune configured below the minimum of %d MB. Please use a higher number.</source> + <translation>剪定が最小値の %d MB以下に設定されています。もっと大きな値を使用してください。</translation> + </message> + <message> <source>Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect)</source> <translation>保有するピアアドレスが少ない場合、DNS ルックアップによりピアアドレスを問い合わせる (-connect を使っていない場合の初期値: 1)</translation> </message> <message> + <source>Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)</source> + <translation>認証情報をプロキシー接続ごとにランダム化する。これによりTorストリーム分離をすることができます (規定値: %u)</translation> + </message> + <message> + <source>Require high priority for relaying free or low-fee transactions (default: %u)</source> + <translation>無料や低い手数料のトランザクションのリレーに際し、高い優先度を要求する (規定値: %u)</translation> + </message> + <message> <source>Set maximum size of high-priority/low-fee transactions in bytes (default: %d)</source> <translation>最優先/最低手数料の最大サイズをバイトで指定 (初期値: %d)</translation> </message> @@ -2928,6 +3118,10 @@ <translation>コイン生成が有効になっていた場合の利用スレッド数を設定する (-1 = すべてのコア, 初期値: %d)</translation> </message> <message> + <source>The transaction amount is too small to send after the fee has been deducted</source> + <translation>手数料差引後のトランザクションの金額が小さすぎるため、送金できません。</translation> + </message> + <message> <source>This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit <https://www.openssl.org/> and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard.</source> <translation>この製品はOpenSSLプロジェクトにより開発されたソフトウェアをOpenSSLツールキットとして利用しています <https://www.openssl.org/>。また、Eric Young氏により開発された暗号ソフトウェア、Thomas Bernard氏により書かれたUPnPソフトウェアを用いています。</translation> </message> @@ -2968,10 +3162,30 @@ rpcpassword=%s <translation>ホワイトリストのピアはDoSによるアクセス禁止処理が無効化され、トランザクションは例えmempool内に既に存在していたとしても常にリレーされます。これは例えばゲートウェイに対して有用です</translation> </message> <message> + <source>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source> + <translation>非剪定モードに戻るためには-reindexオプションを使用してデータベースを再構築する必要があります。これによりブロックチェイン全体の再ダウンロードが行われます。</translation> + </message> + <message> + <source>(default: %u)</source> + <translation>(規定値: %u)</translation> + </message> + <message> <source>Accept public REST requests (default: %u)</source> <translation>公開 REST リクエストを許可する (初期値: %u)</translation> </message> <message> + <source>Activating best chain...</source> + <translation>最優良のチェインを有効化しています...</translation> + </message> + <message> + <source>Allow self signed root certificates (default: 0)</source> + <translation>自己署名ルート証明書を許可する (規定値: 0)</translation> + </message> + <message> + <source>Can't run with a wallet in prune mode.</source> + <translation>剪定モードではウォレット機能付きで起動できません。</translation> + </message> + <message> <source>Cannot resolve -whitebind address: '%s'</source> <translation>-whitebind アドレス '%s' を解決できません</translation> </message> @@ -3068,6 +3282,10 @@ rpcpassword=%s <translation><n>個のネットワークメッセージごとにひとつをランダムに改変する</translation> </message> <message> + <source>Rebuild block chain index from current blk000??.dat files on startup</source> + <translation>起動時に現在の blk000??.dat ファイルからブロック チェーンのインデックスを再構築</translation> + </message> + <message> <source>Send trace/debug info to console instead of debug.log file</source> <translation>トレース/デバッグ情報を debug.log ファイルの代わりにコンソールへ送る</translation> </message> @@ -3104,6 +3322,10 @@ rpcpassword=%s <translation>最小化された状態で起動する</translation> </message> <message> + <source>The transaction amount is too small to pay the fee</source> + <translation>トランザクションの金額が小さすぎて手数料を支払えません</translation> + </message> + <message> <source>This is experimental software.</source> <translation>これは実験的なソフトウェアです。</translation> </message> @@ -3124,6 +3346,10 @@ rpcpassword=%s <translation>取引が大き過ぎます</translation> </message> <message> + <source>UI Options:</source> + <translation>UIオプション:</translation> + </message> + <message> <source>Unable to bind to %s on this computer (bind returned error %s)</source> <translation>このコンピュータの %s にバインドすることができません (バインドが返したエラーは %s)</translation> </message> @@ -3288,6 +3514,10 @@ rpcpassword=%s <translation>ピアの接続数を最大でも <n> 個に維持する (初期値: %u)</translation> </message> <message> + <source>Make the wallet broadcast transactions</source> + <translation>ウォレットのトランザクションをブロードキャストする</translation> + </message> + <message> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)</source> <translation>接続毎の最大受信バッファ <n>*1000 バイト (初期値: %u)</translation> </message> diff --git a/src/qt/locale/bitcoin_ka.ts b/src/qt/locale/bitcoin_ka.ts index 0b96a18d18..b9e118a620 100644 --- a/src/qt/locale/bitcoin_ka.ts +++ b/src/qt/locale/bitcoin_ka.ts @@ -981,11 +981,7 @@ <source>Your current total balance</source> <translation>თქვენი სრული მიმდინარე ბალანსი</translation> </message> - <message> - <source>out of sync</source> - <translation>არ არის სინქრონიზებული</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> <message> diff --git a/src/qt/locale/bitcoin_ko_KR.ts b/src/qt/locale/bitcoin_ko_KR.ts index d9ce8ea1be..42eb9eedbb 100644 --- a/src/qt/locale/bitcoin_ko_KR.ts +++ b/src/qt/locale/bitcoin_ko_KR.ts @@ -1025,11 +1025,7 @@ <source>Your current balance in watch-only addresses</source> <translation>모니터링 지갑의 현재 잔액</translation> </message> - <message> - <source>out of sync</source> - <translation>동기화 필요</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> <message> diff --git a/src/qt/locale/bitcoin_ky.ts b/src/qt/locale/bitcoin_ky.ts index 7d4511b57d..8edee19c70 100644 --- a/src/qt/locale/bitcoin_ky.ts +++ b/src/qt/locale/bitcoin_ky.ts @@ -149,11 +149,7 @@ </context> <context> <name>OverviewPage</name> - <message> - <source>out of sync</source> - <translation>синхрондоштурулган эмес</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> </context> diff --git a/src/qt/locale/bitcoin_la.ts b/src/qt/locale/bitcoin_la.ts index 8f03a20cf6..3e25cf95b6 100644 --- a/src/qt/locale/bitcoin_la.ts +++ b/src/qt/locale/bitcoin_la.ts @@ -601,11 +601,7 @@ <source>Mined balance that has not yet matured</source> <translation>Fossum pendendum quod nondum maturum est</translation> </message> - <message> - <source>out of sync</source> - <translation>non synchronizato</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> <message> diff --git a/src/qt/locale/bitcoin_lt.ts b/src/qt/locale/bitcoin_lt.ts index b96afbbcc9..01fa94bab3 100644 --- a/src/qt/locale/bitcoin_lt.ts +++ b/src/qt/locale/bitcoin_lt.ts @@ -737,11 +737,7 @@ <source>Your current total balance</source> <translation>Jūsų balansas</translation> </message> - <message> - <source>out of sync</source> - <translation>nesinchronizuota</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> <message> diff --git a/src/qt/locale/bitcoin_lv_LV.ts b/src/qt/locale/bitcoin_lv_LV.ts index a46ab83c6b..25f92b6642 100644 --- a/src/qt/locale/bitcoin_lv_LV.ts +++ b/src/qt/locale/bitcoin_lv_LV.ts @@ -933,11 +933,7 @@ <source>Your current total balance</source> <translation>Jūsu kopējā tekošā bilance</translation> </message> - <message> - <source>out of sync</source> - <translation>nav sinhronizēts</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> <message> diff --git a/src/qt/locale/bitcoin_nb.ts b/src/qt/locale/bitcoin_nb.ts index c42cf52212..6e2b4e9fcc 100644 --- a/src/qt/locale/bitcoin_nb.ts +++ b/src/qt/locale/bitcoin_nb.ts @@ -431,6 +431,10 @@ <translation>Ingen kilde for blokker tilgjengelig...</translation> </message> <message numerus="yes"> + <source>Processed %n block(s) of transaction history.</source> + <translation><numerusform>Lastet %n blokk med transaksjonshistorikk.</numerusform><numerusform>Lastet %n blokker med transaksjonshistorikk.</numerusform></translation> + </message> + <message numerus="yes"> <source>%n hour(s)</source> <translation><numerusform>%n time</numerusform><numerusform>%n timer</numerusform></translation> </message> @@ -977,6 +981,10 @@ <translation>Minimer i stedet for å avslutte applikasjonen når vinduet lukkes. Når dette er valgt, vil applikasjonen avsluttes kun etter at Avslutte er valgt i menyen.</translation> </message> <message> + <source>The user interface language can be set here. This setting will take effect after restarting Bitcoin Core.</source> + <translation>Språk for brukergrensesnittet kan velges her. Denne innstillingen trer i kraft etter omstart av Bitcoin Core.</translation> + </message> + <message> <source>Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</source> <translation>Tredjepart URLer (f. eks. en blokkutforsker) som dukker opp i transaksjonsfanen som kontekst meny elementer. %s i URLen er erstattet med transaksjonen sin hash. Flere URLer er separert av en vertikal linje |.</translation> </message> @@ -1001,6 +1009,14 @@ <translation>&Nettverk</translation> </message> <message> + <source>Automatically start Bitcoin Core after logging in to the system.</source> + <translation>Start Bitcoin Core automatisk ved oppstart av datamaskinen.</translation> + </message> + <message> + <source>&Start Bitcoin Core on system login</source> + <translation>&Start Bitcoin Core ved oppstart av datamaskinen</translation> + </message> + <message> <source>(0 = auto, <0 = leave that many cores free)</source> <translation>(0 = automatisk, <0 = la så mange kjerner være ledig)</translation> </message> @@ -1113,6 +1129,10 @@ <translation>Omstart av klienten er nødvendig for å aktivere endringene.</translation> </message> <message> + <source>Client will be shut down. Do you want to proceed?</source> + <translation>Klienten vil bli lukket. Ønsker du å gå videre?</translation> + </message> + <message> <source>This change would require a client restart.</source> <translation>Denne endringen krever omstart av klienten.</translation> </message> @@ -1195,10 +1215,6 @@ <source>Current total balance in watch-only addresses</source> <translation>Nåværende totale balanse i kun observerbare adresser</translation> </message> - <message> - <source>out of sync</source> - <translation>ute av synk</translation> - </message> </context> <context> <name>PaymentServer</name> @@ -1251,10 +1267,18 @@ <translation>Betalingsetterspørringsfil kan ikke leses! Dette kan være forårsaket av en ugyldig betalingsetterspørringsfil.</translation> </message> <message> + <source>Payment request expired.</source> + <translation>Betalingsetterspørringen har utløpt.</translation> + </message> + <message> <source>Unverified payment requests to custom payment scripts are unsupported.</source> <translation>Uverifiserte betalingsforespørsler til egentilpassede betalingscript er ikke støttet.</translation> </message> <message> + <source>Invalid payment request.</source> + <translation>Ugyldig betalingsetterspørring.</translation> + </message> + <message> <source>Refund from %1</source> <translation>Refundering fra %1</translation> </message> @@ -1294,6 +1318,10 @@ <translation>Brukeragent</translation> </message> <message> + <source>Node/Service</source> + <translation>Node/Tjeneste</translation> + </message> + <message> <source>Ping Time</source> <translation>Ping-tid</translation> </message> @@ -1415,6 +1443,10 @@ <translation>Nåværende antall blokker</translation> </message> <message> + <source>Open the Bitcoin Core debug log file from the current data directory. This can take a few seconds for large log files.</source> + <translation>Åpne Bitcoin Core sin loggfil for feilsøk fra gjeldende datamappe. Dette kan ta noen sekunder for store loggfiler.</translation> + </message> + <message> <source>Received</source> <translation>Mottatt</translation> </message> @@ -1483,6 +1515,10 @@ <translation>Ping-tid</translation> </message> <message> + <source>Time Offset</source> + <translation>Tidsforskyvning</translation> + </message> + <message> <source>Last block time</source> <translation>Tidspunkt for siste blokk</translation> </message> @@ -1527,6 +1563,10 @@ <translation>Tøm konsoll</translation> </message> <message> + <source>Welcome to the Bitcoin Core RPC console.</source> + <translation>Velkommen til Bitcoin Core sin RPC-konsoll.</translation> + </message> + <message> <source>Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen.</source> <translation>Bruk opp og ned pil for å navigere historikken, og <b>Ctrl-L</b> for å tømme skjermen.</translation> </message> @@ -1967,10 +2007,30 @@ <translation>Transaksjonen ble avvist! Dette kan skje hvis noen av myntene i lommeboken allerede er brukt, som hvis du kopierte wallet.dat og mynter ble brukt i kopien uten å bli markert som brukt her.</translation> </message> <message> + <source>A fee higher than %1 is considered an absurdly high fee.</source> + <translation>Et gebyr høyere enn %1 er ansett som et absurd høyt gebyr.</translation> + </message> + <message> + <source>Payment request expired.</source> + <translation>Betalingsetterspørringen har utløpt.</translation> + </message> + <message numerus="yes"> + <source>Estimated to begin confirmation within %n block(s).</source> + <translation><numerusform>Anslått til å begynne bekreftelse innen %n blokk.</numerusform><numerusform>Anslått til å begynne bekreftelse innen %n blokker.</numerusform></translation> + </message> + <message> <source>Pay only the minimum fee of %1</source> <translation>Betal kun minimumsgebyret på %1</translation> </message> <message> + <source>The recipient address is not valid. Please recheck.</source> + <translation>Mottakeradressen er ikke gyldig. Vennligst kontroller på nytt.</translation> + </message> + <message> + <source>Duplicate address found: addresses should only be used once each.</source> + <translation>Gjenbruk av adresse funnet: adresser skal bare brukes en gang hver.</translation> + </message> + <message> <source>Warning: Invalid Bitcoin address</source> <translation>Advarsel: Ugyldig Bitcoin-adresse</translation> </message> @@ -2042,10 +2102,26 @@ <translation>Fjern denne oppføringen</translation> </message> <message> + <source>The fee will be deducted from the amount being sent. The recipient will receive less bitcoins than you enter in the amount field. If multiple recipients are selected, the fee is split equally.</source> + <translation>Gebyret vil bli trukket fra beløpet som blir sendt. Mottakeren vil motta mindre bitcoins enn det du skriver inn i beløpsfeltet. Hvis det er valgt flere mottakere, deles gebyret likt.</translation> + </message> + <message> + <source>S&ubtract fee from amount</source> + <translation>T&rekk fra gebyr fra beløp</translation> + </message> + <message> <source>Message:</source> <translation>Melding:</translation> </message> <message> + <source>This is an unauthenticated payment request.</source> + <translation>Dette er en uautorisert betalingsetterspørring.</translation> + </message> + <message> + <source>This is an authenticated payment request.</source> + <translation>Dette er en autorisert betalingsetterspørring.</translation> + </message> + <message> <source>Enter a label for this address to add it to the list of used addresses</source> <translation>Skriv inn en merkelapp for denne adressen for å legge den til listen av brukte adresser</translation> </message> @@ -2084,6 +2160,10 @@ <translation>&Signer Melding</translation> </message> <message> + <source>You can sign messages/agreements with your addresses to prove you can receive bitcoins sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to.</source> + <translation>Du kan signere meldinger/avtaler med adresser for å bevise at du kan motta bitcoins sendt til dem. Vær forsiktig med å signere noe vagt eller tilfeldig, siden phishing-angrep kan prøve å lure deg til å signere din identitet over til dem. Bare signer fullt detaljerte utsagn som du er enig i.</translation> + </message> + <message> <source>The Bitcoin address to sign the message with</source> <translation>Bitcoin-adressen meldingen skal signeres med</translation> </message> @@ -2136,6 +2216,10 @@ <translation>&Verifiser Melding</translation> </message> <message> + <source>Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction!</source> + <translation>Skriv inn mottakerens adresse, melding (forsikre deg om at du kopier linjeskift, mellomrom, faner osv. nøyaktig) og underskrift nedenfor for å bekrefte meldingen. Vær forsiktig så du ikke leser mer ut av signaturen enn hva som er i den signerte meldingen i seg selv, for å unngå å bli lurt av et man-in-the-middle-angrep. Merk at dette bare beviser at den som signerer kan motta med adressen, dette beviser ikke hvem som har sendt transaksjoner!</translation> + </message> + <message> <source>The Bitcoin address the message was signed with</source> <translation>Bitcoin-adressen meldingen ble signert med</translation> </message> @@ -2487,6 +2571,10 @@ <translation>Hvorvidt en kun observerbar adresse er involvert i denne transaksjonen.</translation> </message> <message> + <source>User-defined intent/purpose of the transaction.</source> + <translation>Brukerdefinert intensjon/hensikt med transaksjonen.</translation> + </message> + <message> <source>Amount removed from or added to balance.</source> <translation>Beløp fjernet eller lagt til saldo.</translation> </message> @@ -2729,6 +2817,10 @@ <translation>Bind til angitt adresse. Bruk [vertsmaskin]:port notasjon for IPv6</translation> </message> <message> + <source>Continuously rate-limit free transactions to <n>*1000 bytes per minute (default: %u)</source> + <translation>Ratebegrens gratistransaksjoner kontinuerlig til <n>*1000 bytes per minutt (standardverdi: %u)</translation> + </message> + <message> <source>Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup</source> <translation>Slett alle transaksjoner i lommeboken og gjenopprett kun de delene av blokkjeden gjennom -rescan ved oppstart</translation> </message> @@ -2749,6 +2841,14 @@ <translation>I denne modusen kontrollerer -genproclimit hvor mange blokker som genereres øyeblikkelig.</translation> </message> <message> + <source>Maximum total fees to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)</source> + <translation>Maksimalt samlede gebyrer til å bruke i en enkelt lommeboktransaksjon; settes dette for lavt kan store transaksjoner kanskje avbrytes (standardverdi: %s)</translation> + </message> + <message> + <source>Reduce storage requirements by pruning (deleting) old blocks. This mode disables wallet support and is incompatible with -txindex. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> + <translation>Reduser lagringsbehovet ved beskjæring (slette) gamle blokker. Denne modusen deaktiverer støtte for lommebok og er ikke kompatibel med -txindex. Advarsel: Tilbakestilling av denne innstillingen krever at hele blokkjeden må lastes ned på nytt. (Standardverdi: 0 = deaktiver beskjæringsblokker, >%u = mål for størrelse i MiB å bruke for blokkfiler)</translation> + </message> + <message> <source>Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)</source> <translation>Angi antall tråder for skriptverifisering (%u til %d, 0 = auto, <0 = la det antallet kjerner være ledig, standard: %d)</translation> </message> @@ -2921,6 +3021,10 @@ <translation>Valg for lommebok:</translation> </message> <message> + <source>Warning: This version is obsolete; upgrade required!</source> + <translation>Advarsel: Denne versjonen er utdatert; oppgradering er påkrevd!</translation> + </message> + <message> <source>You need to rebuild the database using -reindex to change -txindex</source> <translation>Du må gjenoppbygge databasen med å bruke -reindex for å endre -txindex</translation> </message> @@ -2953,6 +3057,10 @@ <translation>Opprett nye filer med standardtillatelser i systemet, i stedet for umask 077 (kun virksom med lommebokfunksjonalitet slått av)</translation> </message> <message> + <source>Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)</source> + <translation>Oppdag egne IP-adresser (standardverdi: 1 ved lytting og ingen -externalip eller -proxy)</translation> + </message> + <message> <source>Error: Listening for incoming connections failed (listen returned error %s)</source> <translation>Feil: Lytting etter innkommende tilkoblinger feilet (lytting returnerte feil %s)</translation> </message> @@ -2993,6 +3101,14 @@ <translation>Søk etter nodeadresser via DNS-oppslag, hvis vi har få adresser å koble til (standard: 1 med mindre -connect)</translation> </message> <message> + <source>Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)</source> + <translation>Bruk tilfeldig identitet for hver proxytilkobling. Dette muliggjør TOR stream isolasjon (standardverdi: %u)</translation> + </message> + <message> + <source>Require high priority for relaying free or low-fee transactions (default: %u)</source> + <translation>Krev høy prioritet for videresending av gratistransaksjoner eller transaksjoner med lavt gebyr (standardverdi: %u)</translation> + </message> + <message> <source>Set maximum size of high-priority/low-fee transactions in bytes (default: %d)</source> <translation>Sett maksimum størrelse for transaksjoner med høy prioritet / lavt gebyr, i bytes (standardverdi: %d)</translation> </message> @@ -3001,6 +3117,10 @@ <translation>Angi antall tråder for mynt generering hvis aktivert (-1 = alle kjerner, standardverdi: %d)</translation> </message> <message> + <source>The transaction amount is too small to send after the fee has been deducted</source> + <translation>Transaksjonsbeløpet er for lite til å sendes etter at gebyret er fratrukket</translation> + </message> + <message> <source>This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit <https://www.openssl.org/> and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard.</source> <translation>Dette produktet inneholder programvare utviklet av OpenSSL Project for bruk i OpenSSL Toolkit <https://www.openssl.org/> og kryptografisk programvare skrevet av Eric Young og UPnP-programvare skrevet av Thomas Bernard.</translation> </message> @@ -3053,6 +3173,14 @@ for eksempel: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com <translation>Godta offentlige REST forespørsler (standardverdi: %u)</translation> </message> <message> + <source>Activating best chain...</source> + <translation>Aktiverer beste kjede...</translation> + </message> + <message> + <source>Allow self signed root certificates (default: 0)</source> + <translation>Tillat selvsignerte rotsertifikater (standardverdi: 0)</translation> + </message> + <message> <source>Can't run with a wallet in prune mode.</source> <translation>Kan ikke kjøre med en lommebok i beskjæringsmodus.</translation> </message> @@ -3153,6 +3281,10 @@ for eksempel: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com <translation>Slumpvis bland 1 av hver <n> nettverksmeldinger</translation> </message> <message> + <source>Rebuild block chain index from current blk000??.dat files on startup</source> + <translation>Gjenopprett blokkjedeindeks fra gjeldende blk000??.dat filer ved oppstart</translation> + </message> + <message> <source>Send trace/debug info to console instead of debug.log file</source> <translation>Send spor-/feilsøkingsinformasjon til konsollen istedenfor filen debug.log</translation> </message> @@ -3189,6 +3321,10 @@ for eksempel: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com <translation>Start minimert</translation> </message> <message> + <source>The transaction amount is too small to pay the fee</source> + <translation>Transaksjonsbeløpet er for lite til å betale gebyr</translation> + </message> + <message> <source>This is experimental software.</source> <translation>Dette er eksperimentell programvare.</translation> </message> @@ -3377,6 +3513,10 @@ for eksempel: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com <translation>Hold maks <n> koblinger åpne til andre noder (standardverdi: %u)</translation> </message> <message> + <source>Make the wallet broadcast transactions</source> + <translation>Få lommeboken til å kringkaste transaksjoner</translation> + </message> + <message> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)</source> <translation>Maks mottaksbuffer per forbindelse, <n>*1000 bytes (standardverdi: %u)</translation> </message> diff --git a/src/qt/locale/bitcoin_nl.ts b/src/qt/locale/bitcoin_nl.ts index 3e1d568d20..385972845a 100644 --- a/src/qt/locale/bitcoin_nl.ts +++ b/src/qt/locale/bitcoin_nl.ts @@ -168,6 +168,10 @@ <translation>Weet u zeker dat u uw portemonnee wilt versleutelen?</translation> </message> <message> + <source>Bitcoin Core will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> + <translation>Bitcoin Core zal nu afsluiten om het versleutelingsproces te voltooien. Hou er rekening mee dat versleuteling van je portemonnee je niet volledig beschermt tegen diefstal van jouw bitcoins door malware op je computer.</translation> + </message> + <message> <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> <translation>BELANGRIJK: Elke eerder gemaakte backup van uw portemonneebestand dient u te vervangen door het nieuw gegenereerde, versleutelde portemonneebestand. Om veiligheidsredenen zullen eerdere backups van het niet-versleutelde portemonneebestand onbruikbaar worden zodra u uw nieuwe, versleutelde, portemonnee begint te gebruiken.</translation> </message> @@ -181,7 +185,11 @@ </message> <message> <source>Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>.</source> - <translation>Voer een nieuw wachtwoord in voor uw portomonee.<br/>Gebruik een wachtwoord van <b>tien of meer willekeurige karakters</b>, of <b>acht of meer woorden</b>.</translation> + <translation>Voer een nieuw wachtwoord in voor uw portemonnee.<br/>Gebruik een wachtwoord van <b>tien of meer willekeurige karakters</b>, of <b>acht of meer woorden</b>.</translation> + </message> + <message> + <source>Enter the old passphrase and new passphrase to the wallet.</source> + <translation>Voer het oude en nieuwe wachtwoord in voor uw portemonnee.</translation> </message> <message> <source>Wallet encryption failed</source> @@ -419,6 +427,10 @@ <translation>Geen bron voor blokken beschikbaar...</translation> </message> <message numerus="yes"> + <source>Processed %n block(s) of transaction history.</source> + <translation><numerusform>%n blok aan transactie geschiedenis verwerkt.</numerusform><numerusform>%n blokken aan transactie geschiedenis verwerkt.</numerusform></translation> + </message> + <message numerus="yes"> <source>%n hour(s)</source> <translation><numerusform>%n uur</numerusform><numerusform>%n uur</numerusform></translation> </message> @@ -471,6 +483,36 @@ <translation>Aan het bijwerken...</translation> </message> <message> + <source>Date: %1 +</source> + <translation>Datum: %1 +</translation> + </message> + <message> + <source>Amount: %1 +</source> + <translation>Aantal: %1 +</translation> + </message> + <message> + <source>Type: %1 +</source> + <translation>Type: %1 +</translation> + </message> + <message> + <source>Label: %1 +</source> + <translation>Label: %1 +</translation> + </message> + <message> + <source>Address: %1 +</source> + <translation>Adres: %1 +</translation> + </message> + <message> <source>Sent transaction</source> <translation>Verzonden transactie</translation> </message> @@ -669,6 +711,18 @@ <translation>geen</translation> </message> <message> + <source>This label turns red if the transaction size is greater than 1000 bytes.</source> + <translation>Dit label wordt rood als de transactie groter is dan 1000 bytes.</translation> + </message> + <message> + <source>This label turns red if the priority is smaller than "medium".</source> + <translation>Dit label wordt rood als de prioriteit lager is dan "gemiddeld".</translation> + </message> + <message> + <source>This label turns red if any recipient receives an amount smaller than %1.</source> + <translation>Dit label wordt rood wanneer een ontvanger minder dan %1 krijgt.</translation> + </message> + <message> <source>Can vary +/- %1 satoshi(s) per input.</source> <translation>Kan per input +/- %1 satoshi(s) variëren.</translation> </message> @@ -919,6 +973,10 @@ <translation>IP-adres van de proxy (bijv. IPv4: 127.0.0.1 / IPv6: ::1)</translation> </message> <message> + <source>The user interface language can be set here. This setting will take effect after restarting Bitcoin Core.</source> + <translation>Stel hier de taal van de applicatie in. Deze instelling zal van kracht worden na het herstarten van de applicatie.</translation> + </message> + <message> <source>Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</source> <translation>Derde partijen URL's (bijvoorbeeld block explorer) dat in de transacties tab verschijnen als contextmenu elementen. %s in de URL is vervangen door transactie hash. Verscheidene URL's zijn gescheiden door een verticale streep |. </translation> </message> @@ -943,6 +1001,14 @@ <translation>&Netwerk</translation> </message> <message> + <source>Automatically start Bitcoin Core after logging in to the system.</source> + <translation>Bitcoin Kern automatisch starten bij inloggen.</translation> + </message> + <message> + <source>&Start Bitcoin Core on system login</source> + <translation>&Start Bitcoin Kern tijdens login.</translation> + </message> + <message> <source>(0 = auto, <0 = leave that many cores free)</source> <translation>(0 = auto, <0 = laat dit aantal kernen vrij)</translation> </message> @@ -1055,6 +1121,10 @@ <translation>Herstart van de client is vereist om veranderingen door te voeren.</translation> </message> <message> + <source>Client will be shut down. Do you want to proceed?</source> + <translation>Applicatie zal worden afgesloten. Wilt u doorgaan?</translation> + </message> + <message> <source>This change would require a client restart.</source> <translation>Om dit aan te passen moet de client opnieuw gestart worden.</translation> </message> @@ -1137,10 +1207,6 @@ <source>Current total balance in watch-only addresses</source> <translation>Huidige balans in alleen-bekijkbare adressen.</translation> </message> - <message> - <source>out of sync</source> - <translation>niet gesynchroniseerd</translation> - </message> </context> <context> <name>PaymentServer</name> diff --git a/src/qt/locale/bitcoin_pam.ts b/src/qt/locale/bitcoin_pam.ts index 503528f4a7..54c30dfb6f 100644 --- a/src/qt/locale/bitcoin_pam.ts +++ b/src/qt/locale/bitcoin_pam.ts @@ -593,11 +593,7 @@ <source>Your current total balance</source> <translation>Ing kekang kasalungsungan kabuuang balanse</translation> </message> - <message> - <source>out of sync</source> - <translation>ali ya maka-sync</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> </context> diff --git a/src/qt/locale/bitcoin_pl.ts b/src/qt/locale/bitcoin_pl.ts index 7a7285aa01..db49e20cf1 100644 --- a/src/qt/locale/bitcoin_pl.ts +++ b/src/qt/locale/bitcoin_pl.ts @@ -3,7 +3,7 @@ <name>AddressBookPage</name> <message> <source>Right-click to edit address or label</source> - <translation>Prawy klik żeby edytować adres lub etykietę</translation> + <translation>Kliknij prawy przycisk aby edytować adres lub etykietę</translation> </message> <message> <source>Create a new address</source> @@ -1097,10 +1097,6 @@ <source>Current total balance in watch-only addresses</source> <translation>Łączna kwota na podglądanych adresach</translation> </message> - <message> - <source>out of sync</source> - <translation>nie zsynchronizowany</translation> - </message> </context> <context> <name>PaymentServer</name> diff --git a/src/qt/locale/bitcoin_pt_BR.ts b/src/qt/locale/bitcoin_pt_BR.ts index d4d1d83e54..42a781de99 100644 --- a/src/qt/locale/bitcoin_pt_BR.ts +++ b/src/qt/locale/bitcoin_pt_BR.ts @@ -63,7 +63,7 @@ </message> <message> <source>Receiving addresses</source> - <translation>Endereços para receber</translation> + <translation>Endereços de recebimento</translation> </message> <message> <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source> @@ -224,7 +224,7 @@ <name>BitcoinGUI</name> <message> <source>Sign &message...</source> - <translation>&Assinar Mensagem...</translation> + <translation>&Assinar mensagem...</translation> </message> <message> <source>Synchronizing with network...</source> @@ -284,11 +284,11 @@ </message> <message> <source>&Sending addresses...</source> - <translation>Enviando endereço&s...</translation> + <translation>Endereço&s de envio...</translation> </message> <message> <source>&Receiving addresses...</source> - <translation>&Receber endereços...</translation> + <translation>Endereços de &Recebimento...</translation> </message> <message> <source>Open &URI...</source> @@ -380,7 +380,7 @@ </message> <message> <source>&Help</source> - <translation>&Ajuda</translation> + <translation>A&juda</translation> </message> <message> <source>Tabs toolbar</source> @@ -977,6 +977,10 @@ <translation>Minimizar em vez de fechar o programa quando a janela for fechada. Quando essa opção estiver ativa, o programa só será fechado somente pela opção Sair no menu Arquivo.</translation> </message> <message> + <source>The user interface language can be set here. This setting will take effect after restarting Bitcoin Core.</source> + <translation>A linguagem da interface do usuário pode ser alterada aqui. A mudança ocorrerá após o reinício do Bitcoin Core.</translation> + </message> + <message> <source>Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</source> <translation>URLs de terceiros (exemplo: explorador de blocos) que aparecem na aba de transações como itens do menu de contexto. %s na URL é substituido pela hash da transação. Múltiplas URLs são separadas pela barra vertical |.</translation> </message> @@ -1207,10 +1211,6 @@ <source>Current total balance in watch-only addresses</source> <translation>Balanço total em endereços monitorados</translation> </message> - <message> - <source>out of sync</source> - <translation>fora de sincronia</translation> - </message> </context> <context> <name>PaymentServer</name> @@ -1388,7 +1388,7 @@ </message> <message> <source>Debug window</source> - <translation>Janela de debug</translation> + <translation>Janela de depuração</translation> </message> <message> <source>General</source> @@ -2121,7 +2121,7 @@ </message> <message> <source>&Sign Message</source> - <translation>&Assinar Mensagem</translation> + <translation>&Assinar mensagem</translation> </message> <message> <source>The Bitcoin address to sign the message with</source> @@ -2161,7 +2161,7 @@ </message> <message> <source>Sign &Message</source> - <translation>Assinar &Mensagem</translation> + <translation>Assinar &mensagem</translation> </message> <message> <source>Reset all sign message fields</source> @@ -2173,7 +2173,7 @@ </message> <message> <source>&Verify Message</source> - <translation>&Verificar Mensagem</translation> + <translation>&Verificar mensagem</translation> </message> <message> <source>The Bitcoin address the message was signed with</source> @@ -2185,7 +2185,7 @@ </message> <message> <source>Verify &Message</source> - <translation>Verificar &Mensagem</translation> + <translation>Verificar &mensagem</translation> </message> <message> <source>Reset all verify message fields</source> @@ -2193,7 +2193,7 @@ </message> <message> <source>Click "Sign Message" to generate signature</source> - <translation>Clique em "Assinar Mensagem" para gerar a assinatura</translation> + <translation>Clique em "Assinar mensagem" para gerar a assinatura</translation> </message> <message> <source>The entered address is invalid.</source> @@ -2846,7 +2846,7 @@ </message> <message> <source>Debugging/Testing options:</source> - <translation>Opções de Debug/Teste:</translation> + <translation>Opções de depuração/teste:</translation> </message> <message> <source>Do not load the wallet and disable wallet RPC calls</source> @@ -2886,7 +2886,7 @@ </message> <message> <source>If <category> is not supplied, output all debugging information.</source> - <translation>Se <category> não for informada, logar toda informação de debug.</translation> + <translation>Se <category> não for informada, registrar toda informação de depuração.</translation> </message> <message> <source>Importing...</source> @@ -2945,6 +2945,10 @@ <translation>Opções da carteira:</translation> </message> <message> + <source>Warning: This version is obsolete; upgrade required!</source> + <translation>Atenção: Essa versão está obsoleta, atualização necessária!</translation> + </message> + <message> <source>You need to rebuild the database using -reindex to change -txindex</source> <translation>Você precisa reconstruir o banco de dados utilizando -reindex</translation> </message> @@ -3110,7 +3114,7 @@ por exemplo: alertnotify=echo %%s | mail -s "Alerta do Bitcoin" admin@foo.com.br </message> <message> <source>Show all debugging options (usage: --help -help-debug)</source> - <translation>Exibir todas opções de debug (uso: --help -help-debug)</translation> + <translation>Exibir todas opções de depuração (uso: --help -help-debug)</translation> </message> <message> <source>Show splash screen on startup (default: 1)</source> @@ -3238,7 +3242,7 @@ por exemplo: alertnotify=echo %%s | mail -s "Alerta do Bitcoin" admin@foo.com.br </message> <message> <source>Include IP addresses in debug output (default: %u)</source> - <translation>Incluir endereço IP na saída de debug (padrão: %u)</translation> + <translation>Incluir endereço IP na saída de depuração (padrão: %u)</translation> </message> <message> <source>Invalid -proxy address: '%s'</source> diff --git a/src/qt/locale/bitcoin_pt_PT.ts b/src/qt/locale/bitcoin_pt_PT.ts index 30ac9fdf31..5012ff8d83 100644 --- a/src/qt/locale/bitcoin_pt_PT.ts +++ b/src/qt/locale/bitcoin_pt_PT.ts @@ -1138,10 +1138,6 @@ <source>Current total balance in watch-only addresses</source> <translation>Saldo disponivél em enderços modo-verificação</translation> </message> - <message> - <source>out of sync</source> - <translation>fora de sincronia</translation> - </message> </context> <context> <name>PaymentServer</name> diff --git a/src/qt/locale/bitcoin_ro_RO.ts b/src/qt/locale/bitcoin_ro_RO.ts index 8b029eeca1..11a29e86c9 100644 --- a/src/qt/locale/bitcoin_ro_RO.ts +++ b/src/qt/locale/bitcoin_ro_RO.ts @@ -168,6 +168,10 @@ <translation>Sigur doriţi să criptaţi portofelul dvs.?</translation> </message> <message> + <source>Bitcoin Core will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> + <translation>Bitcoin se va închide acum pentru a termina procesul de criptare. Ţineţi minte că criptarea portofelului nu vă poate proteja în totalitate de furtul monedelor de către programe dăunătoare care vă infectează calculatorul.</translation> + </message> + <message> <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> <translation>IMPORTANT: Orice copie de siguranţă făcută anterior portofelului dumneavoastră ar trebui înlocuită cu cea generată cel mai recent, fişier criptat al portofelului. Pentru siguranţă, copiile de siguranţă vechi ale portofelului ne-criptat vor deveni inutile imediat ce veţi începe folosirea noului fişier criptat al portofelului.</translation> </message> @@ -184,6 +188,10 @@ <translation>Introduceţi noua parolă a portofelului electronic.<br/>Vă rugăm să folosiţi o parolă de<b>minimum 10 caractere aleatoare</b>, sau <b>minimum 8 cuvinte</b>.</translation> </message> <message> + <source>Enter the old passphrase and new passphrase to the wallet.</source> + <translation>Introduceţi vechea şi noua parolă pentru portofel.</translation> + </message> + <message> <source>Wallet encryption failed</source> <translation>Criptarea portofelului nu a reuşit</translation> </message> @@ -391,6 +399,10 @@ <translation>&Despre Nucleul Bitcoin</translation> </message> <message> + <source>Modify configuration options for Bitcoin Core</source> + <translation>Modifică opţiunile de configurare pentru Bitcoin</translation> + </message> + <message> <source>Show the list of used sending addresses and labels</source> <translation>Arată lista de adrese trimise şi etichetele folosite.</translation> </message> @@ -419,6 +431,10 @@ <translation>Nici o sursă de bloc disponibilă...</translation> </message> <message numerus="yes"> + <source>Processed %n block(s) of transaction history.</source> + <translation><numerusform>S-a procesat %n bloc din istoricul tranzacţiilor.</numerusform><numerusform>S-au procesat %n blocuri din istoricul tranzacţiilor.</numerusform><numerusform>S-au procesat %n de blocuri din istoricul tranzacţiilor.</numerusform></translation> + </message> + <message numerus="yes"> <source>%n hour(s)</source> <translation><numerusform>%n oră</numerusform><numerusform>%n ore</numerusform><numerusform>%n ore</numerusform></translation> </message> @@ -471,6 +487,36 @@ <translation>Se actualizează...</translation> </message> <message> + <source>Date: %1 +</source> + <translation>Data: %1 +</translation> + </message> + <message> + <source>Amount: %1 +</source> + <translation>Sumă: %1 +</translation> + </message> + <message> + <source>Type: %1 +</source> + <translation>Tip: %1 +</translation> + </message> + <message> + <source>Label: %1 +</source> + <translation>Etichetă: %1 +</translation> + </message> + <message> + <source>Address: %1 +</source> + <translation>Adresă: %1 +</translation> + </message> + <message> <source>Sent transaction</source> <translation>Tranzacţie expediată</translation> </message> @@ -665,6 +711,18 @@ <translation>nimic</translation> </message> <message> + <source>This label turns red if the transaction size is greater than 1000 bytes.</source> + <translation>Această etichetă devine roşie în cazul în care dimensiunea tranzacţiei este mai mare de 1000 de octeţi.</translation> + </message> + <message> + <source>This label turns red if the priority is smaller than "medium".</source> + <translation>Această etichetă devine roşie dacă prioritatea e mai mică decît "medie".</translation> + </message> + <message> + <source>This label turns red if any recipient receives an amount smaller than %1.</source> + <translation>Această etichetă devine roşie, dacă orice beneficiar primeşte o sumă mai mică decât %1.</translation> + </message> + <message> <source>Can vary +/- %1 satoshi(s) per input.</source> <translation>Poate varia +/- %1 satoshi pentru fiecare intrare.</translation> </message> @@ -907,6 +965,14 @@ <translation>Adresa IP a serverului proxy (de exemplu: IPv4: 127.0.0.1 / IPv6: ::1)</translation> </message> <message> + <source>Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</source> + <translation>Minimizează fereastra în locul părăsirii programului în momentul închiderii ferestrei. Cînd acestă opţiune e activă, aplicaţia se va opri doar în momentul selectării comenzii 'Închide aplicaţia' din menu.</translation> + </message> + <message> + <source>The user interface language can be set here. This setting will take effect after restarting Bitcoin Core.</source> + <translation>Limba interfeţei utilizatorului poate fi setată aici. Această setare va avea efect după repornirea Nucleului Bitcoin.</translation> + </message> + <message> <source>Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</source> <translation>URL-uri terţe părţi (de exemplu, un explorator de bloc), care apar în tab-ul tranzacţiilor ca elemente de meniu contextual. %s în URL este înlocuit cu hash de tranzacţie. URL-urile multiple sînt separate prin bară verticală |.</translation> </message> @@ -931,6 +997,10 @@ <translation>Reţea</translation> </message> <message> + <source>&Start Bitcoin Core on system login</source> + <translation>Porneşte Nucleul Bitcoin la pornirea sistemului</translation> + </message> + <message> <source>(0 = auto, <0 = leave that many cores free)</source> <translation>(0 = automat, <0 = lasă atîtea nuclee libere)</translation> </message> @@ -1043,6 +1113,10 @@ <translation>Este necesară repornirea clientului pentru a activa schimbările.</translation> </message> <message> + <source>Client will be shut down. Do you want to proceed?</source> + <translation>Clientul va fi închis. Doriţi să continuaţi?</translation> + </message> + <message> <source>This change would require a client restart.</source> <translation>Această schimbare necesită o repornire a clientului.</translation> </message> @@ -1125,10 +1199,6 @@ <source>Current total balance in watch-only addresses</source> <translation>Soldul dvs. total în adresele doar-supraveghere</translation> </message> - <message> - <source>out of sync</source> - <translation>nesincronizat</translation> - </message> </context> <context> <name>PaymentServer</name> @@ -1169,6 +1239,10 @@ <translation>URL-ul cererii de plată preluat nu este valid: %1</translation> </message> <message> + <source>URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source> + <translation>URI nu poate fi analizat! Acest lucru poate fi cauzat de o adresă Bitcoin nevalidă sau parametri URI deformaţi.</translation> + </message> + <message> <source>Payment request file handling</source> <translation>Manipulare fişier cerere de plată</translation> </message> @@ -1177,10 +1251,18 @@ <translation>Fişierul cerere de plată nu poate fi citit! Cauza poate fi un fişier cerere de plată nevalid.</translation> </message> <message> + <source>Payment request expired.</source> + <translation>Cererea de plată a expirat.</translation> + </message> + <message> <source>Unverified payment requests to custom payment scripts are unsupported.</source> <translation>Cererile de plată neverificate prin script-uri personalizate de plată nu sînt suportate.</translation> </message> <message> + <source>Invalid payment request.</source> + <translation>Cerere de plată nevalidă.</translation> + </message> + <message> <source>Refund from %1</source> <translation>Rambursare de la %1</translation> </message> @@ -1441,6 +1523,10 @@ <translation>Curăţă consola</translation> </message> <message> + <source>Welcome to the Bitcoin Core RPC console.</source> + <translation>Bun venit la consola Nucleului Bitcoin RPC.</translation> + </message> + <message> <source>Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen.</source> <translation>Folosiţi săgetile sus şi jos pentru a naviga în istoric şi <b>Ctrl-L</b> pentru a curăţa.</translation> </message> @@ -1729,6 +1815,10 @@ <translation>per kilooctet</translation> </message> <message> + <source>Hide</source> + <translation>Ascunde</translation> + </message> + <message> <source>total at least</source> <translation>total cel puţin</translation> </message> @@ -1857,10 +1947,18 @@ <translation>Tranzacţia a fost respinsă! Acest lucru se poate întîmpla dacă o parte din monedele tale din portofel au fost deja cheltuite, la fel ca şi cum aţi fi folosit o copie a wallet.dat şi monedele au fost cheltuite în copie, dar nu au fost marcate ca şi cheltuite şi aici.</translation> </message> <message> + <source>Payment request expired.</source> + <translation>Cererea de plată a expirat.</translation> + </message> + <message> <source>Pay only the minimum fee of %1</source> <translation>Plăteşte doar taxa minimă de %1</translation> </message> <message> + <source>The recipient address is not valid. Please recheck.</source> + <translation>Adresa destinatarului nu este validă, vă rugăm să o verificaţi.</translation> + </message> + <message> <source>Warning: Invalid Bitcoin address</source> <translation>Atenţie: Adresa bitcoin nevalidă!</translation> </message> diff --git a/src/qt/locale/bitcoin_ru.ts b/src/qt/locale/bitcoin_ru.ts index 30d44b090f..c208b3e25e 100644 --- a/src/qt/locale/bitcoin_ru.ts +++ b/src/qt/locale/bitcoin_ru.ts @@ -1105,10 +1105,6 @@ <source>Current total balance in watch-only addresses</source> <translation>Текущий общий баланс на адресах наблюдения</translation> </message> - <message> - <source>out of sync</source> - <translation>не синхронизировано</translation> - </message> </context> <context> <name>PaymentServer</name> diff --git a/src/qt/locale/bitcoin_sk.ts b/src/qt/locale/bitcoin_sk.ts index 294eca4127..8e7d38be00 100644 --- a/src/qt/locale/bitcoin_sk.ts +++ b/src/qt/locale/bitcoin_sk.ts @@ -1081,11 +1081,7 @@ <source>Recent transactions</source> <translation>Nedávne transakcie</translation> </message> - <message> - <source>out of sync</source> - <translation>nesynchronizované</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> <message> diff --git a/src/qt/locale/bitcoin_sl_SI.ts b/src/qt/locale/bitcoin_sl_SI.ts index 7a283bcca1..abbdba3760 100644 --- a/src/qt/locale/bitcoin_sl_SI.ts +++ b/src/qt/locale/bitcoin_sl_SI.ts @@ -929,11 +929,7 @@ <source>Your current total balance</source> <translation>Vaše trenutno skupno stanje</translation> </message> - <message> - <source>out of sync</source> - <translation>iz sinhronizacije</translation> - </message> -</context> + </context> <context> <name>PaymentServer</name> <message> diff --git a/src/qt/locale/bitcoin_sv.ts b/src/qt/locale/bitcoin_sv.ts index 8a46ae8470..289074f134 100644 --- a/src/qt/locale/bitcoin_sv.ts +++ b/src/qt/locale/bitcoin_sv.ts @@ -1212,10 +1212,6 @@ Var vänlig och försök igen.</translation> <source>Current total balance in watch-only addresses</source> <translation>Nuvarande total balans i granska-bara adresser</translation> </message> - <message> - <source>out of sync</source> - <translation>osynkroniserad</translation> - </message> </context> <context> <name>PaymentServer</name> @@ -3270,6 +3266,10 @@ till exempel: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com <translation>Slupmässigt brus 1 gång varje <n> nätverksmeddelande</translation> </message> <message> + <source>Rebuild block chain index from current blk000??.dat files on startup</source> + <translation>Återskapa blockkedjans index från nuvarande blk000??.dat filer under uppstarten</translation> + </message> + <message> <source>Send trace/debug info to console instead of debug.log file</source> <translation>Skicka trace-/debuginformation till terminalen istället för till debug.log</translation> </message> diff --git a/src/qt/locale/bitcoin_tr.ts b/src/qt/locale/bitcoin_tr.ts index 66902a515b..bf6f3f2791 100644 --- a/src/qt/locale/bitcoin_tr.ts +++ b/src/qt/locale/bitcoin_tr.ts @@ -168,6 +168,10 @@ <translation>Cüzdanınızı şifrelemek istediğinizden emin misiniz?</translation> </message> <message> + <source>Bitcoin Core will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> + <translation>Şifreleme işlemini tamamlamak için Bitcoin Çekirdeği şimdi kapanacaktır. Cüzdanınızı şifrelemenin, Bitcoinlerinizin bilgisayara bulaşan kötücül bir yazılım tarafından çalınmaya karşı tamamen koruyamayacağını unutmayınız.</translation> + </message> + <message> <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> <translation>ÖNEMLİ: Önceden yapmış olduğunuz cüzdan dosyası yedeklemelerinin yeni oluşturulan şifrelenmiş cüzdan dosyası ile değiştirilmeleri gerekir. Güvenlik nedenleriyle yeni, şifrelenmiş cüzdanı kullanmaya başladığınızda eski şifrelenmemiş cüzdan dosyaları işe yaramaz hale gelecektir.</translation> </message> @@ -184,6 +188,10 @@ <translation>Cüzdan için yeni parolayı giriniz.<br/>Lütfen <b>on ya da daha fazla rastgele karakter</b> veya <b>sekiz ya da daha fazla kelime</b> içeren bir parola kullanınız.</translation> </message> <message> + <source>Enter the old passphrase and new passphrase to the wallet.</source> + <translation>Cüzdan için eski parolayı ve yeni parolayı giriniz.</translation> + </message> + <message> <source>Wallet encryption failed</source> <translation>Cüzdan şifrelemesi başarısız oldu</translation> </message> @@ -391,6 +399,10 @@ <translation>Bitcoin Çekirdeği &hakkında</translation> </message> <message> + <source>Modify configuration options for Bitcoin Core</source> + <translation>Bitcoin Çekirdeği yapılandırma seçeneklerini değiştir</translation> + </message> + <message> <source>Show the list of used sending addresses and labels</source> <translation>Kullanılmış gönderme adresleri ve etiketlerin listesini göster</translation> </message> @@ -419,6 +431,10 @@ <translation>Hiçbir blok kaynağı mevcut değil...</translation> </message> <message numerus="yes"> + <source>Processed %n block(s) of transaction history.</source> + <translation><numerusform>Muamele tarihçesinden %n blok işlendi.</numerusform><numerusform>Muamele tarihçesinden %n blok işlendi</numerusform></translation> + </message> + <message numerus="yes"> <source>%n hour(s)</source> <translation><numerusform>%n saat</numerusform><numerusform>%n saat</numerusform></translation> </message> @@ -471,6 +487,36 @@ <translation>Aralık kapatılıyor...</translation> </message> <message> + <source>Date: %1 +</source> + <translation>Tarih: %1 +</translation> + </message> + <message> + <source>Amount: %1 +</source> + <translation>Meblağ: %1 +</translation> + </message> + <message> + <source>Type: %1 +</source> + <translation>Tür: %1 +</translation> + </message> + <message> + <source>Label: %1 +</source> + <translation>Etiket: %1 +</translation> + </message> + <message> + <source>Address: %1 +</source> + <translation>Adres: %1 +</translation> + </message> + <message> <source>Sent transaction</source> <translation>Muamele yollandı</translation> </message> @@ -669,6 +715,18 @@ <translation>boş</translation> </message> <message> + <source>This label turns red if the transaction size is greater than 1000 bytes.</source> + <translation>Eğer muamele boyutu 1000 bayttan yüksek ise bu etiket kırmızı hale gelir.</translation> + </message> + <message> + <source>This label turns red if the priority is smaller than "medium".</source> + <translation>Eğer öncelik "ortadan" düşükse bu etiket kırmızı olur.</translation> + </message> + <message> + <source>This label turns red if any recipient receives an amount smaller than %1.</source> + <translation>Eğer herhangi bir alıcı %1'den düşük bir meblağ alırsa bu etiket kırmızı olur.</translation> + </message> + <message> <source>Can vary +/- %1 satoshi(s) per input.</source> <translation>Giriş başına +/- %1 satoshi olarak değişebilir.</translation> </message> @@ -919,6 +977,14 @@ <translation>Vekil sunucusunun IP adresi (mesela IPv4: 127.0.0.1 / IPv6: ::1)</translation> </message> <message> + <source>Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</source> + <translation>Pencere kapatıldığında uygulamadan çıkmak yerine uygulamayı küçültür. Bu seçenek etkinleştirildiğinde, uygulama sadece menüden çıkış seçildiğinde kapanacaktır.</translation> + </message> + <message> + <source>The user interface language can be set here. This setting will take effect after restarting Bitcoin Core.</source> + <translation>Kullanıcı arayüzünün dili burada belirtilebilir. Bu ayar Bitcoin Çekirdeği tekrar başlatıldığında etkinleşecektir.</translation> + </message> + <message> <source>Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</source> <translation>Muameleler sekmesinde bağlam menüsü unsurları olarak görünen üçüncü taraf bağlantıları (mesela bir blok tarayıcısı). URL'deki %s, muamele hash değeri ile değiştirilecektir. Birden çok bağlantılar düşey çubuklar | ile ayrılacaktır.</translation> </message> @@ -943,6 +1009,14 @@ <translation>&Şebeke</translation> </message> <message> + <source>Automatically start Bitcoin Core after logging in to the system.</source> + <translation>Sistemde oturum açıldığında Bitcoin Çekirdeğini otomatik olarak başlat.</translation> + </message> + <message> + <source>&Start Bitcoin Core on system login</source> + <translation>Bitcoin Çekirdeğini sistem oturumuyla &başlat</translation> + </message> + <message> <source>(0 = auto, <0 = leave that many cores free)</source> <translation>(0 = otomatik, <0 = bu kadar çekirdeği kullanma)</translation> </message> @@ -1055,6 +1129,10 @@ <translation>Değişikliklerin uygulanması için istemcinin yeniden başlatılması lazımdır.</translation> </message> <message> + <source>Client will be shut down. Do you want to proceed?</source> + <translation>İstemci kapanacaktır. Devam etmek istiyor musunuz?</translation> + </message> + <message> <source>This change would require a client restart.</source> <translation>Bu değişiklik istemcinin tekrar başlatılmasını gerektirir.</translation> </message> @@ -1137,10 +1215,6 @@ <source>Current total balance in watch-only addresses</source> <translation>Sadece izlenen adreslerdeki güncel toplam bakiye</translation> </message> - <message> - <source>out of sync</source> - <translation>eşleşme dışı</translation> - </message> </context> <context> <name>PaymentServer</name> @@ -1193,14 +1267,26 @@ <translation>Ödeme talebi okunamaz ya da işlenemez! Bunun sebebi geçersiz bir ödeme talebi dosyası olabilir.</translation> </message> <message> + <source>Payment request expired.</source> + <translation>Ödeme talebinin ömrü doldu.</translation> + </message> + <message> <source>Unverified payment requests to custom payment scripts are unsupported.</source> <translation>Özel ödeme betiklerine teyit edilmemiş ödeme talepleri desteklenmez.</translation> </message> <message> + <source>Invalid payment request.</source> + <translation>Geçersiz ödeme talebi.</translation> + </message> + <message> <source>Refund from %1</source> <translation>%1 öğesinden iade</translation> </message> <message> + <source>Payment request %1 is too large (%2 bytes, allowed %3 bytes).</source> + <translation>%1 ödeme talebi çok büyük (%2 bayt, müsaade edilen %3 bayt).</translation> + </message> + <message> <source>Payment request DoS protection</source> <translation>Ödeme talebi DoS koruması</translation> </message> @@ -1232,6 +1318,10 @@ <translation>Kullanıcı Yazılımı</translation> </message> <message> + <source>Node/Service</source> + <translation>Düğüm/Servis</translation> + </message> + <message> <source>Ping Time</source> <translation>Ping Zamanı</translation> </message> @@ -1353,6 +1443,10 @@ <translation>Güncel blok sayısı</translation> </message> <message> + <source>Open the Bitcoin Core debug log file from the current data directory. This can take a few seconds for large log files.</source> + <translation>Güncel veri klasöründen Bitcoin Çekirdeği hata ayıklama kütük dosyasını açar. Büyük kütük dosyaları için bu birkaç saniye alabilir.</translation> + </message> + <message> <source>Received</source> <translation>Alınan</translation> </message> @@ -1421,6 +1515,10 @@ <translation>Ping Zamanı</translation> </message> <message> + <source>Time Offset</source> + <translation>Saat Farkı</translation> + </message> + <message> <source>Last block time</source> <translation>Son blok zamanı</translation> </message> @@ -1465,6 +1563,10 @@ <translation>Konsolu temizle</translation> </message> <message> + <source>Welcome to the Bitcoin Core RPC console.</source> + <translation>Bitcoin Çekirdeği RPC konsoluna hoş geldiniz.</translation> + </message> + <message> <source>Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen.</source> <translation>Tarihçede gezinmek için imleç tuşlarını kullanınız, <b>Ctrl-L</b> ile de ekranı temizleyebilirsiniz.</translation> </message> @@ -1761,6 +1863,10 @@ <translation>Eğer özel ücret 1000 satoşi olarak ayarlandıysa ve muamele sadece 250 baytsa, "kilobayt başı" ücret olarak sadece 250 satoşi öder ve "toplam asgari" 1000 satoşi öder. Bir kilobayttan yüksek muameleler için ikisi de kilobayt başı ödeme yapar.</translation> </message> <message> + <source>Hide</source> + <translation>Sakla</translation> + </message> + <message> <source>total at least</source> <translation>toplam asgari</translation> </message> @@ -1901,10 +2007,26 @@ <translation>Muamele reddedildi! Cüzdanınızdaki madenî paraların bazıları zaten harcanmış olduğunda bu meydana gelebilir. Örneğin wallet.dat dosyasının bir kopyasını kullandıysanız ve kopyada para harcandığında ancak burada harcandığı işaretlenmediğinde.</translation> </message> <message> + <source>A fee higher than %1 is considered an absurdly high fee.</source> + <translation>%1 tutarından yüksek ücret saçma derecede yüksek bir ücret olarak kabul edilir.</translation> + </message> + <message> + <source>Payment request expired.</source> + <translation>Ödeme talebinin ömrü doldu.</translation> + </message> + <message> <source>Pay only the minimum fee of %1</source> <translation>Sadece asgari ücret olan %1 tutarını öde</translation> </message> <message> + <source>The recipient address is not valid. Please recheck.</source> + <translation>Alıcı adresi geçerli değildir. Lütfen denetleyiniz.</translation> + </message> + <message> + <source>Duplicate address found: addresses should only be used once each.</source> + <translation>Çift adres bulundu: adresler herbiri için sadece bir kez kullanılmalıdır.</translation> + </message> + <message> <source>Warning: Invalid Bitcoin address</source> <translation>Uyarı: geçersiz Bitcoin adresi</translation> </message> @@ -1976,10 +2098,26 @@ <translation>Bu unsuru kaldır</translation> </message> <message> + <source>The fee will be deducted from the amount being sent. The recipient will receive less bitcoins than you enter in the amount field. If multiple recipients are selected, the fee is split equally.</source> + <translation>Ücret yollanan meblağdan alınacaktır. Alıcı meblağ alanında girdiğinizden daha az bitcoin alacaktır. Eğer birden çok alıcı seçiliyse ücret eşit olarak bölünecektir.</translation> + </message> + <message> + <source>S&ubtract fee from amount</source> + <translation>Ücreti meblağdan düş</translation> + </message> + <message> <source>Message:</source> <translation>Mesaj:</translation> </message> <message> + <source>This is an unauthenticated payment request.</source> + <translation>Bu, kimliği doğrulanmamış bir ödeme talebidir.</translation> + </message> + <message> + <source>This is an authenticated payment request.</source> + <translation>Bu, kimliği doğrulanmış bir ödeme talebidir.</translation> + </message> + <message> <source>Enter a label for this address to add it to the list of used addresses</source> <translation>Kullanılmış adres listesine eklemek için bu adrese bir etiket girin</translation> </message> @@ -2018,6 +2156,10 @@ <translation>Mesaj &imzala</translation> </message> <message> + <source>You can sign messages/agreements with your addresses to prove you can receive bitcoins sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to.</source> + <translation>Adreslerinize yollanan bitcoinleri alabileceğiniz ispatlamak için adreslerinizle mesaj/anlaşma imzalayabilirsiniz. Oltalama saldırılarının kimliğinizi imzanızla elde etmeyi deneyebilecekleri için belirsiz ya da rastgele hiçbir şey imzalamamaya dikkat ediniz. Sadece ayrıntılı açıklaması olan ve tümüne katıldığınız ifadeleri imzalayınız.</translation> + </message> + <message> <source>The Bitcoin address to sign the message with</source> <translation>Mesajın imzalanmasında kullanılacak Bitcoin adresi</translation> </message> @@ -2070,6 +2212,10 @@ <translation>Mesaj &kontrol et</translation> </message> <message> + <source>Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction!</source> + <translation>Alıcının adresini, mesajı (satır sonları, boşluklar, sekmeler vs. karakterleri tam olarak kopyaladığınızdan emin olunuz) ve imzayı aşağıda giriniz. Bir ortadaki adam saldırısı tarafından kandırılmaya mâni olmak için imzadan, imzalı mesajın içeriğini aşan bir anlam çıkarmamaya dikkat ediniz. Bunun sadece imzalayan tarafın adres ile alım yapabildiğini ispatladığını ve herhangi bir muamelenin gönderi tarafını kanıtlayamayacağını unutmayınız!</translation> + </message> + <message> <source>The Bitcoin address the message was signed with</source> <translation>Mesajın imzalanmasında kullanılan Bitcoin adresi</translation> </message> @@ -2421,6 +2567,10 @@ <translation>Bu muamelede sadece izlenen bir adresin bulunup bulunmadığı.</translation> </message> <message> + <source>User-defined intent/purpose of the transaction.</source> + <translation>Muamelenin kullanıcı tanımlı niyeti/amacı.</translation> + </message> + <message> <source>Amount removed from or added to balance.</source> <translation>Bakiyeden alınan ya da bakiyeye eklenen meblağ.</translation> </message> @@ -2663,6 +2813,10 @@ <translation>Belirtilen adrese bağlan ve daima ondan dinle. IPv6 için [makine]:port yazımını kullanınız</translation> </message> <message> + <source>Continuously rate-limit free transactions to <n>*1000 bytes per minute (default: %u)</source> + <translation>Devamlı olarak ücretsiz muameleleri dakikada <n>*1000 bayt olarak sınırla (varsayılan: %u)</translation> + </message> + <message> <source>Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup</source> <translation>Tüm cüzdan muamelelerini sil ve başlangıçta -rescan ile sadece blok zincirinin parçası olanları geri getir</translation> </message> @@ -2683,6 +2837,14 @@ <translation>Bu kipte -genproclimit kaç sayıda bloğun anında oluşturulduğunu kontrol eder.</translation> </message> <message> + <source>Maximum total fees to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)</source> + <translation>Tek cüzdan muamelesinde kullanılacak azami toplam ücret; bunu çok düşük olarak ayarlamak büyük muameleleri iptal edebilir (varsayılan: %s)</translation> + </message> + <message> + <source>Reduce storage requirements by pruning (deleting) old blocks. This mode disables wallet support and is incompatible with -txindex. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> + <translation>Depolama gerekliliğini eski blokları silerek düşür. Bu kip cüzdan desteğini devre dışı bırakır ve -txindex ile uyumsuzdur. İkaz: Bu ayarı geri almak tüm blok zincirini yeniden indirmeyi gerektirir. (varsayılan: 0 = blokları silmeyi devre dışı bırak, >%u = MB olarak blok dosyaları için kullanılacak hedef boyut)</translation> + </message> + <message> <source>Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)</source> <translation>Betik kontrolü iş parçacıklarının sayısını belirler (%u ilâ %d, 0 = otomatik, <0 = bu sayıda çekirdeği kullanma, varsayılan: %d)</translation> </message> @@ -2811,6 +2973,14 @@ <translation>Sadece <net> şebekesindeki düğümlere bağlan (ipv4, ipv6 veya onion)</translation> </message> <message> + <source>Prune cannot be configured with a negative value.</source> + <translation>Prune negatif bir değerle yapılandırılamaz.</translation> + </message> + <message> + <source>Prune mode is incompatible with -txindex.</source> + <translation>Prune kipi -txindex ile uyumsuzdur.</translation> + </message> + <message> <source>Set database cache size in megabytes (%d to %d, default: %d)</source> <translation>Veritabanı önbellek boyutunu megabayt olarak belirt (%d ilâ %d, varsayılan: %d)</translation> </message> @@ -2847,6 +3017,10 @@ <translation>Cüzdan seçenekleri:</translation> </message> <message> + <source>Warning: This version is obsolete; upgrade required!</source> + <translation>Uyarı: Bu sürüm çok eskidir; güncellemeniz gerekir!</translation> + </message> + <message> <source>You need to rebuild the database using -reindex to change -txindex</source> <translation>-txindex'i değiştirmek için veritabanını -reindex kullanarak tekrar inşa etmeniz gerekmektedir</translation> </message> @@ -2879,6 +3053,10 @@ <translation>Yeni dosyaları umask 077 yerine varsayılan izinlerle oluştur (sadece devre dışı cüzdan işlevselliği ile etkilidir)</translation> </message> <message> + <source>Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)</source> + <translation>Kendi IP adreslerini keşfet (varsayılan: dinlenildiğinde ve -externalip ya da -proxy yoksa 1)</translation> + </message> + <message> <source>Error: Listening for incoming connections failed (listen returned error %s)</source> <translation>Hata: İçeri gelen bağlantıların dinlenmesi başarısız oldu (dinleme %s hatasını verdi)</translation> </message> @@ -2911,10 +3089,22 @@ <translation>Aktardığımız ve oluşturduğumuz veri taşıyıcı muamelelerindeki azami veri boyutu (varsayılan: %u)</translation> </message> <message> + <source>Prune configured below the minimum of %d MB. Please use a higher number.</source> + <translation>Prune, asgari değer olan %d MB'den düşük olarak ayarlanmıştır. Lütfen daha yüksek bir sayı kullanınız.</translation> + </message> + <message> <source>Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect)</source> <translation>Adres sayısı azaldıysa DNS sorgulamasıyla eş adresleri ara (varsayılan: 1 -connect kullanılmadıysa)</translation> </message> <message> + <source>Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)</source> + <translation>Her vekil bağlantısı için kimlik verilerini rastgele yap. Bu, Tor akış izolasyonunu etkinleştirir (varsayılan: %u)</translation> + </message> + <message> + <source>Require high priority for relaying free or low-fee transactions (default: %u)</source> + <translation>Ücretsiz ya da düşük ücretli muamelelerin geçişi için yüksek öncelik iste (varsayılan: %u)</translation> + </message> + <message> <source>Set maximum size of high-priority/low-fee transactions in bytes (default: %d)</source> <translation>Yüksek öncelikli/düşük ücretli muamelelerin azami boyutunu bayt olarak ayarla (varsayılan: %d)</translation> </message> @@ -2923,6 +3113,10 @@ <translation>Etkinse bitcoin oluşuturulmasına atanan iş parçacığı sayısını ayarla (-1 = tüm çekirdekler, varsayılan: %d)</translation> </message> <message> + <source>The transaction amount is too small to send after the fee has been deducted</source> + <translation>Bu muamele, ücret düşüldükten sonra göndermek için çok düşük</translation> + </message> + <message> <source>This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit <https://www.openssl.org/> and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard.</source> <translation>Bu ürün OpenSSL projesi tarafından OpenSSL araç takımı (http://www.openssl.org/) için geliştirilen yazılımlar, Eric Young (eay@cryptsoft.com) tarafından hazırlanmış şifreleme yazılımları ve Thomas Bernard tarafından programlanmış UPnP yazılımı içerir.</translation> </message> @@ -2963,10 +3157,30 @@ mesela: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com <translation>Beyaz listeye alınan eşler DoS yasaklamasına uğramazlar ve muameleleri zaten mempool'da olsalar da daima aktarılır, bu mesela bir geçit için kullanışlıdır</translation> </message> <message> + <source>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source> + <translation>Prune olmayan kipe dönmek için veritabanını -reindex ile tekrar derlemeniz gerekir. Bu, tüm blok zincirini tekrar indirecektir</translation> + </message> + <message> + <source>(default: %u)</source> + <translation>(varsayılan: %u)</translation> + </message> + <message> <source>Accept public REST requests (default: %u)</source> <translation>Herkese açık REST taleplerini kabul et (varsayılan: %u)</translation> </message> <message> + <source>Activating best chain...</source> + <translation>En iyi zincir etkinleştiriliyor...</translation> + </message> + <message> + <source>Allow self signed root certificates (default: 0)</source> + <translation>Kendinden imzalı kök sertifikalara müsaade et (varsayılan: 0)</translation> + </message> + <message> + <source>Can't run with a wallet in prune mode.</source> + <translation>Prune kipindeki bir cüzdan ile çalışamaz.</translation> + </message> + <message> <source>Cannot resolve -whitebind address: '%s'</source> <translation>-whitebind adresi çözümlenemedi: '%s'</translation> </message> @@ -2991,6 +3205,10 @@ mesela: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com <translation>wallet.dat dosyasının yüklenmesinde hata: Cüzdan Bitcoin Çekirdeğinin daha yeni bir sürümünü gerektirmektedir</translation> </message> <message> + <source>Error reading from database, shutting down.</source> + <translation>Veritabanından okumada hata, kapatılıyor.</translation> + </message> + <message> <source>Error: Unsupported argument -tor found, use -onion.</source> <translation>Hata: Deskteklenmeyen -tor argümanı bulundu, -onion kullanınız.</translation> </message> @@ -3047,6 +3265,10 @@ mesela: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com <translation>RPC sunucu seçenekleri:</translation> </message> <message> + <source>RPC support for HTTP persistent connections (default: %d)</source> + <translation>Kalıcı HTTP bağlantıları için RPC desteği (varsayılan: %d)</translation> + </message> + <message> <source>Randomly drop 1 of every <n> network messages</source> <translation>Her <n> şebeke mesajından rastgele 1 mesajı görmezden gel</translation> </message> @@ -3055,6 +3277,10 @@ mesela: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com <translation>Her <n> şebeke mesajından rastgele birini bulanıklaştır</translation> </message> <message> + <source>Rebuild block chain index from current blk000??.dat files on startup</source> + <translation>Başlangıçta blok zinciri indeksini güncel blk000??.dat dosyalarından tekrar inşa et</translation> + </message> + <message> <source>Send trace/debug info to console instead of debug.log file</source> <translation>Trace/hata ayıklama verilerini debug.log dosyası yerine konsola gönder</translation> </message> @@ -3091,6 +3317,10 @@ mesela: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com <translation>Küçültülmüş olarak başlat</translation> </message> <message> + <source>The transaction amount is too small to pay the fee</source> + <translation>Muamele meblağı ücreti ödemek için çok düşük</translation> + </message> + <message> <source>This is experimental software.</source> <translation>Bu, deneysel bir yazılımdır.</translation> </message> @@ -3111,6 +3341,10 @@ mesela: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com <translation>Muamele çok büyük</translation> </message> <message> + <source>UI Options:</source> + <translation>Arayüz Seçenkleri:</translation> + </message> + <message> <source>Unable to bind to %s on this computer (bind returned error %s)</source> <translation>Bu bilgisayarda %s unsuruna bağlanılamadı (bağlanma %s hatasını verdi)</translation> </message> @@ -3275,6 +3509,10 @@ mesela: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo.com <translation>Eşler ile en çok <n> adet bağlantı kur (varsayılan: %u)</translation> </message> <message> + <source>Make the wallet broadcast transactions</source> + <translation>Cüzdanın muameleleri yayınlamasını sağla</translation> + </message> + <message> <source>Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)</source> <translation>Her bağlantı için azami alım tamponu, <n>*1000 bayt (varsayılan: %u)</translation> </message> diff --git a/src/qt/locale/bitcoin_uk.ts b/src/qt/locale/bitcoin_uk.ts index b2e67e296e..92e0cc75c4 100644 --- a/src/qt/locale/bitcoin_uk.ts +++ b/src/qt/locale/bitcoin_uk.ts @@ -440,7 +440,7 @@ </message> <message> <source>%1 behind</source> - <translation>%1 позаду</translation> + <translation>%1 тому</translation> </message> <message> <source>Last received block was generated %1 ago.</source> @@ -1123,7 +1123,7 @@ </message> <message> <source>Recent transactions</source> - <translation>Недавні транзакції</translation> + <translation>Останні транзакції</translation> </message> <message> <source>Unconfirmed transactions to watch-only addresses</source> @@ -1137,10 +1137,6 @@ <source>Current total balance in watch-only addresses</source> <translation>Поточний сукупний баланс в адресах для спостереження</translation> </message> - <message> - <source>out of sync</source> - <translation>не синхронізовано</translation> - </message> </context> <context> <name>PaymentServer</name> diff --git a/src/qt/locale/bitcoin_zh_CN.ts b/src/qt/locale/bitcoin_zh_CN.ts index 4c9f287e95..3bcce1faab 100644 --- a/src/qt/locale/bitcoin_zh_CN.ts +++ b/src/qt/locale/bitcoin_zh_CN.ts @@ -1215,10 +1215,6 @@ <source>Current total balance in watch-only addresses</source> <translation>观察地址(watch-only address)中的当前总余额 </translation> </message> - <message> - <source>out of sync</source> - <translation>数据同步中</translation> - </message> </context> <context> <name>PaymentServer</name> @@ -2018,6 +2014,10 @@ <source>Payment request expired.</source> <translation>支付请求已过期。</translation> </message> + <message numerus="yes"> + <source>Estimated to begin confirmation within %n block(s).</source> + <translation><numerusform>预计 %n 个数据块后被确认。</numerusform></translation> + </message> <message> <source>Pay only the minimum fee of %1</source> <translation>只支付最小费用 %1</translation> @@ -2208,6 +2208,10 @@ <translation>验证消息(&V)</translation> </message> <message> + <source>Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction!</source> + <translation>请在下面输入接收者地址、消息(确保换行符、空格符、制表符等完全相同)和签名以验证消息。请仔细核对签名信息,以提防中间人攻击。请注意,这只是证明接收方签名的地址,它不能证明任何交易!</translation> + </message> + <message> <source>The Bitcoin address the message was signed with</source> <translation>消息使用的签名地址</translation> </message> @@ -2811,6 +2815,10 @@ <translation>绑定指定的IP地址开始监听。IPv6地址请使用[host]:port 格式</translation> </message> <message> + <source>Continuously rate-limit free transactions to <n>*1000 bytes per minute (default: %u)</source> + <translation>自由交易不断的速率限制为<n>*1000 字节每分钟(默认值: %u)</translation> + </message> + <message> <source>Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup</source> <translation>删除钱包的所有交易记录,且只有用 -rescan参数启动客户端才能重新取回交易记录 </translation> </message> @@ -3267,6 +3275,10 @@ rpcpassword=%s <translation>随机每1个模拟测试<n>网络信息</translation> </message> <message> + <source>Rebuild block chain index from current blk000??.dat files on startup</source> + <translation>启动时重新为当前的 blk000??.dat 文件建立索引</translation> + </message> + <message> <source>Send trace/debug info to console instead of debug.log file</source> <translation>跟踪/调试信息输出到控制台,不输出到 debug.log 文件</translation> </message> diff --git a/src/qt/locale/bitcoin_zh_TW.ts b/src/qt/locale/bitcoin_zh_TW.ts index 254b33d5a4..3792a76095 100644 --- a/src/qt/locale/bitcoin_zh_TW.ts +++ b/src/qt/locale/bitcoin_zh_TW.ts @@ -1137,10 +1137,6 @@ <source>Current total balance in watch-only addresses</source> <translation>所有只能看位址的目前全部餘額</translation> </message> - <message> - <source>out of sync</source> - <translation>還沒同步</translation> - </message> </context> <context> <name>PaymentServer</name> diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 4fa15db9c6..2e33b9adf8 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -18,8 +18,8 @@ #include <QAbstractItemDelegate> #include <QPainter> -#define DECORATION_SIZE 64 -#define NUM_ITEMS 3 +#define DECORATION_SIZE 54 +#define NUM_ITEMS 5 class TxViewDelegate : public QAbstractItemDelegate { @@ -121,6 +121,12 @@ OverviewPage::OverviewPage(QWidget *parent) : { ui->setupUi(this); + // use a SingleColorIcon for the "out of sync warning" icon + QIcon icon = SingleColorIcon(":/icons/warning"); + icon.addPixmap(icon.pixmap(QSize(64,64), QIcon::Normal), QIcon::Disabled); // also set the disabled icon because we are using a disabled QPushButton to work around missing HiDPI support of QLabel (https://bugreports.qt.io/browse/QTBUG-42503) + ui->labelTransactionsStatus->setIcon(icon); + ui->labelWalletStatus->setIcon(icon); + // Recent transactions ui->listTransactions->setItemDelegate(txdelegate); ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE)); @@ -129,10 +135,6 @@ OverviewPage::OverviewPage(QWidget *parent) : connect(ui->listTransactions, SIGNAL(clicked(QModelIndex)), this, SLOT(handleTransactionClicked(QModelIndex))); - // init "out of sync" warning labels - ui->labelWalletStatus->setText("(" + tr("out of sync") + ")"); - ui->labelTransactionsStatus->setText("(" + tr("out of sync") + ")"); - // start with displaying the "out of sync" warnings showOutOfSyncWarning(true); } diff --git a/src/qt/res/bitcoin-qt-res.rc b/src/qt/res/bitcoin-qt-res.rc index c0f3e2fb39..9f66d0af79 100644 --- a/src/qt/res/bitcoin-qt-res.rc +++ b/src/qt/res/bitcoin-qt-res.rc @@ -19,7 +19,7 @@ BEGIN BLOCK "040904E4" // U.S. English - multilingual (hex) BEGIN VALUE "CompanyName", "Bitcoin" - VALUE "FileDescription", "Bitcoin Core (OSS GUI client for Bitcoin)" + VALUE "FileDescription", "Bitcoin Core (GUI node for Bitcoin)" VALUE "FileVersion", VER_FILEVERSION_STR VALUE "InternalName", "bitcoin-qt" VALUE "LegalCopyright", COPYRIGHT_STR diff --git a/src/qt/res/icons/warning.png b/src/qt/res/icons/warning.png Binary files differnew file mode 100644 index 0000000000..723a30a658 --- /dev/null +++ b/src/qt/res/icons/warning.png diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 29c971ec79..e99972d498 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -16,15 +16,16 @@ #include "rpcclient.h" #include "util.h" -#include "json/json_spirit_value.h" - #include <openssl/crypto.h> +#include "univalue/univalue.h" + #ifdef ENABLE_WALLET #include <db_cxx.h> #endif #include <QKeyEvent> +#include <QMenu> #include <QScrollBar> #include <QThread> #include <QTime> @@ -167,21 +168,21 @@ void RPCExecutor::request(const QString &command) std::string strPrint; // Convert argument list to JSON objects in method-dependent way, // and pass it along with the method name to the dispatcher. - json_spirit::Value result = tableRPC.execute( + UniValue result = tableRPC.execute( args[0], RPCConvertValues(args[0], std::vector<std::string>(args.begin() + 1, args.end()))); // Format result reply - if (result.type() == json_spirit::null_type) + if (result.isNull()) strPrint = ""; - else if (result.type() == json_spirit::str_type) + else if (result.isStr()) strPrint = result.get_str(); else - strPrint = write_string(result, true); + strPrint = result.write(2); emit reply(RPCConsole::CMD_REPLY, QString::fromStdString(strPrint)); } - catch (const json_spirit::Object& objError) + catch (UniValue& objError) { try // Nice formatting for standard-format error { @@ -191,7 +192,7 @@ void RPCExecutor::request(const QString &command) } catch (const std::runtime_error&) // raised when converting to invalid type, i.e. missing code or message { // Show raw JSON object - emit reply(RPCConsole::CMD_ERROR, QString::fromStdString(write_string(json_spirit::Value(objError), false))); + emit reply(RPCConsole::CMD_ERROR, QString::fromStdString(objError.write())); } } catch (const std::exception& e) @@ -205,7 +206,8 @@ RPCConsole::RPCConsole(QWidget *parent) : ui(new Ui::RPCConsole), clientModel(0), historyPtr(0), - cachedNodeid(-1) + cachedNodeid(-1), + contextMenu(0) { ui->setupUi(this); GUIUtil::restoreWindowGeometry("nRPCConsoleWindow", this->size(), this); @@ -305,10 +307,22 @@ void RPCConsole::setClientModel(ClientModel *model) ui->peerWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows); ui->peerWidget->setSelectionMode(QAbstractItemView::SingleSelection); + ui->peerWidget->setContextMenuPolicy(Qt::CustomContextMenu); ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH); ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH); ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH); + // create context menu actions + QAction* disconnectAction = new QAction(tr("&Disconnect Node"), this); + + // create context menu + contextMenu = new QMenu(); + contextMenu->addAction(disconnectAction); + + // context menu signals + connect(ui->peerWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showMenu(const QPoint&))); + connect(disconnectAction, SIGNAL(triggered()), this, SLOT(disconnectSelectedNode())); + // connect the peerWidget selection model to our peerSelected() handler connect(ui->peerWidget->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, SLOT(peerSelected(const QItemSelection &, const QItemSelection &))); @@ -659,3 +673,21 @@ void RPCConsole::hideEvent(QHideEvent *event) // stop PeerTableModel auto refresh clientModel->getPeerTableModel()->stopAutoRefresh(); } + +void RPCConsole::showMenu(const QPoint& point) +{ + QModelIndex index = ui->peerWidget->indexAt(point); + if (index.isValid()) + contextMenu->exec(QCursor::pos()); +} + +void RPCConsole::disconnectSelectedNode() +{ + // Get currently selected peer address + QString strNode = GUIUtil::getEntryData(ui->peerWidget, 0, PeerTableModel::Address); + // Find the node, disconnect it and clear the selected node + if (CNode *bannedNode = FindNode(strNode.toStdString())) { + bannedNode->CloseSocketDisconnect(); + ui->peerWidget->selectionModel()->clearSelection(); + } +} diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 8737be35d1..767e9aaeea 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -19,6 +19,7 @@ namespace Ui { } QT_BEGIN_NAMESPACE +class QMenu; class QItemSelection; QT_END_NAMESPACE @@ -57,6 +58,8 @@ private slots: void resizeEvent(QResizeEvent *event); void showEvent(QShowEvent *event); void hideEvent(QHideEvent *event); + /** Show custom context menu on Peers tab */ + void showMenu(const QPoint& point); public slots: void clear(); @@ -73,6 +76,8 @@ public slots: void peerSelected(const QItemSelection &selected, const QItemSelection &deselected); /** Handle updated peer information */ void peerLayoutChanged(); + /** Disconnect a selected node on the Peers tab */ + void disconnectSelectedNode(); signals: // For RPC command executor @@ -98,6 +103,7 @@ private: QStringList history; int historyPtr; NodeId cachedNodeid; + QMenu *contextMenu; }; #endif // BITCOIN_QT_RPCCONSOLE_H diff --git a/src/qt/scicon.cpp b/src/qt/scicon.cpp index a0ffcd82a9..c493b5569e 100644 --- a/src/qt/scicon.cpp +++ b/src/qt/scicon.cpp @@ -11,7 +11,9 @@ #include <QPalette> #include <QPixmap> -static void MakeSingleColorImage(QImage& img, const QColor& colorbase) +namespace { + +void MakeSingleColorImage(QImage& img, const QColor& colorbase) { img = img.convertToFormat(QImage::Format_ARGB32); for (int x = img.width(); x--; ) @@ -24,15 +26,22 @@ static void MakeSingleColorImage(QImage& img, const QColor& colorbase) } } +} + QImage SingleColorImage(const QString& filename, const QColor& colorbase) { QImage img(filename); +#if !defined(WIN32) && !defined(MAC_OSX) MakeSingleColorImage(img, colorbase); +#endif return img; } QIcon SingleColorIcon(const QIcon& ico, const QColor& colorbase) { +#if defined(WIN32) || defined(MAC_OSX) + return ico; +#else QIcon new_ico; QSize sz; Q_FOREACH(sz, ico.availableSizes()) @@ -42,6 +51,7 @@ QIcon SingleColorIcon(const QIcon& ico, const QColor& colorbase) new_ico.addPixmap(QPixmap::fromImage(img)); } return new_ico; +#endif } QIcon SingleColorIcon(const QString& filename, const QColor& colorbase) @@ -51,6 +61,9 @@ QIcon SingleColorIcon(const QString& filename, const QColor& colorbase) QColor SingleColor() { +#if defined(WIN32) || defined(MAC_OSX) + return QColor(0,0,0); +#else const QColor colorHighlightBg(QApplication::palette().color(QPalette::Highlight)); const QColor colorHighlightFg(QApplication::palette().color(QPalette::HighlightedText)); const QColor colorText(QApplication::palette().color(QPalette::WindowText)); @@ -61,6 +74,7 @@ QColor SingleColor() else colorbase = colorHighlightFg; return colorbase; +#endif } QIcon SingleColorIcon(const QString& filename) diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 7a33e3567b..3d57711568 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -590,12 +590,12 @@ void SendCoinsDialog::updateGlobalFeeVariables() { if (ui->radioSmartFee->isChecked()) { - nTxConfirmTarget = (int)25 - (int)std::max(0, std::min(24, ui->sliderSmartFee->value())); + nTxConfirmTarget = defaultConfirmTarget - ui->sliderSmartFee->value(); payTxFee = CFeeRate(0); } else { - nTxConfirmTarget = 25; + nTxConfirmTarget = defaultConfirmTarget; payTxFee = CFeeRate(ui->customFee->value()); fPayAtLeastCustomFee = ui->radioCustomAtLeast->isChecked(); } @@ -629,7 +629,7 @@ void SendCoinsDialog::updateSmartFeeLabel() if(!model || !model->getOptionsModel()) return; - int nBlocksToConfirm = (int)25 - (int)std::max(0, std::min(24, ui->sliderSmartFee->value())); + int nBlocksToConfirm = defaultConfirmTarget - ui->sliderSmartFee->value(); CFeeRate feeRate = mempool.estimateFee(nBlocksToConfirm); if (feeRate <= CFeeRate(0)) // not enough data => minfee { diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 14adb02573..fc513bf2ba 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -23,6 +23,8 @@ QT_BEGIN_NAMESPACE class QUrl; QT_END_NAMESPACE +const int defaultConfirmTarget = 25; + /** Dialog for sending bitcoins */ class SendCoinsDialog : public QDialog { diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 4fffd03adf..5662b16657 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -26,7 +26,7 @@ using namespace std; QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) { AssertLockHeld(cs_main); - if (!IsFinalTx(wtx, chainActive.Height() + 1)) + if (!CheckFinalTx(wtx)) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) return tr("Open for %n more block(s)", "", wtx.nLockTime - chainActive.Height()); diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 7f1db58e5d..15d13e9fc9 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -188,7 +188,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) status.depth = wtx.GetDepthInMainChain(); status.cur_num_blocks = chainActive.Height(); - if (!IsFinalTx(wtx, chainActive.Height() + 1)) + if (!CheckFinalTx(wtx)) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) { diff --git a/src/rest.cpp b/src/rest.cpp index 78139682a6..1fce7dfc9c 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -16,10 +16,11 @@ #include <boost/algorithm/string.hpp> #include <boost/dynamic_bitset.hpp> +#include "univalue/univalue.h" + using namespace std; -using namespace json_spirit; -static const int MAX_GETUTXOS_OUTPOINTS = 100; //allow a max of 100 outpoints to be queried at once +static const int MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once enum RetFormat { RF_UNDEF, @@ -61,9 +62,9 @@ public: string message; }; -extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry); -extern Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false); -extern void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex); +extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); +extern UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false); +extern void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); static RestErr RESTERR(enum HTTPStatusCode status, string message) { @@ -197,6 +198,9 @@ static bool rest_block(AcceptedConnection* conn, throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found"); pblockindex = mapBlockIndex[hash]; + if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) + throw RESTERR(HTTP_NOT_FOUND, hashStr + " not available (pruned data)"); + if (!ReadBlockFromDisk(block, pblockindex)) throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found"); } @@ -218,8 +222,8 @@ static bool rest_block(AcceptedConnection* conn, } case RF_JSON: { - Object objBlock = blockToJSON(block, pblockindex, showTxDetails); - string strJSON = write_string(Value(objBlock), false) + "\n"; + UniValue objBlock = blockToJSON(block, pblockindex, showTxDetails); + string strJSON = objBlock.write() + "\n"; conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush; return true; } @@ -259,13 +263,12 @@ static bool rest_chaininfo(AcceptedConnection* conn, { vector<string> params; const RetFormat rf = ParseDataFormat(params, strURIPart); - + switch (rf) { case RF_JSON: { - Array rpcParams; - Value chainInfoObject = getblockchaininfo(rpcParams, false); - - string strJSON = write_string(chainInfoObject, false) + "\n"; + UniValue rpcParams(UniValue::VARR); + UniValue chainInfoObject = getblockchaininfo(rpcParams, false); + string strJSON = chainInfoObject.write() + "\n"; conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush; return true; } @@ -273,7 +276,7 @@ static bool rest_chaininfo(AcceptedConnection* conn, throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: json)"); } } - + // not reached return true; // continue to process further HTTP reqs on this cxn } @@ -314,9 +317,9 @@ static bool rest_tx(AcceptedConnection* conn, } case RF_JSON: { - Object objTx; + UniValue objTx(UniValue::VOBJ); TxToJSON(tx, hashBlock, objTx); - string strJSON = write_string(Value(objTx), false) + "\n"; + string strJSON = objTx.write() + "\n"; conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush; return true; } @@ -339,18 +342,53 @@ static bool rest_getutxos(AcceptedConnection* conn, vector<string> params; enum RetFormat rf = ParseDataFormat(params, strURIPart); + vector<string> uriParts; + if (params.size() > 0 && params[0].length() > 1) + { + std::string strUriParams = params[0].substr(1); + boost::split(uriParts, strUriParams, boost::is_any_of("/")); + } + // throw exception in case of a empty request - if (strRequest.length() == 0) + if (strRequest.length() == 0 && uriParts.size() == 0) throw RESTERR(HTTP_INTERNAL_SERVER_ERROR, "Error: empty request"); + bool fInputParsed = false; bool fCheckMemPool = false; vector<COutPoint> vOutPoints; // parse/deserialize input // input-format = output-format, rest/getutxos/bin requires binary input, gives binary output, ... - + + if (uriParts.size() > 0) + { + + //inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...) + if (uriParts.size() > 0 && uriParts[0] == "checkmempool") + fCheckMemPool = true; + + for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++) + { + uint256 txid; + int32_t nOutput; + std::string strTxid = uriParts[i].substr(0, uriParts[i].find("-")); + std::string strOutput = uriParts[i].substr(uriParts[i].find("-")+1); + + if (!ParseInt32(strOutput, &nOutput) || !IsHex(strTxid)) + throw RESTERR(HTTP_INTERNAL_SERVER_ERROR, "Parse error"); + + txid.SetHex(strTxid); + vOutPoints.push_back(COutPoint(txid, (uint32_t)nOutput)); + } + + if (vOutPoints.size() > 0) + fInputParsed = true; + else + throw RESTERR(HTTP_INTERNAL_SERVER_ERROR, "Error: empty request"); + } + string strRequestMutable = strRequest; //convert const string to string for allowing hex to bin converting - + switch (rf) { case RF_HEX: { // convert hex to bin, continue then with bin part @@ -360,11 +398,17 @@ static bool rest_getutxos(AcceptedConnection* conn, case RF_BINARY: { try { - //deserialize - CDataStream oss(SER_NETWORK, PROTOCOL_VERSION); - oss << strRequestMutable; - oss >> fCheckMemPool; - oss >> vOutPoints; + //deserialize only if user sent a request + if (strRequestMutable.size() > 0) + { + if (fInputParsed) //don't allow sending input over URI and HTTP RAW DATA + throw RESTERR(HTTP_INTERNAL_SERVER_ERROR, "Combination of URI scheme inputs and raw post data is not allowed"); + + CDataStream oss(SER_NETWORK, PROTOCOL_VERSION); + oss << strRequestMutable; + oss >> fCheckMemPool; + oss >> vOutPoints; + } } catch (const std::ios_base::failure& e) { // abort in case of unreadable binary data throw RESTERR(HTTP_INTERNAL_SERVER_ERROR, "Parse error"); @@ -373,33 +417,8 @@ static bool rest_getutxos(AcceptedConnection* conn, } case RF_JSON: { - try { - // parse json request - Value valRequest; - if (!read_string(strRequest, valRequest)) - throw RESTERR(HTTP_INTERNAL_SERVER_ERROR, "Parse error"); - - Object jsonObject = valRequest.get_obj(); - const Value& checkMempoolValue = find_value(jsonObject, "checkmempool"); - - if (!checkMempoolValue.is_null()) { - fCheckMemPool = checkMempoolValue.get_bool(); - } - const Value& outpointsValue = find_value(jsonObject, "outpoints"); - if (!outpointsValue.is_null()) { - Array outPoints = outpointsValue.get_array(); - BOOST_FOREACH (const Value& outPoint, outPoints) { - Object outpointObject = outPoint.get_obj(); - uint256 txid = ParseHashO(outpointObject, "txid"); - Value nValue = find_value(outpointObject, "n"); - int nOutput = nValue.get_int(); - vOutPoints.push_back(COutPoint(txid, nOutput)); - } - } - } catch (...) { - // return HTTP 500 if there was a json parsing error - throw RESTERR(HTTP_INTERNAL_SERVER_ERROR, "Parse error"); - } + if (!fInputParsed) + throw RESTERR(HTTP_INTERNAL_SERVER_ERROR, "Error: empty request"); break; } default: { @@ -473,7 +492,7 @@ static bool rest_getutxos(AcceptedConnection* conn, } case RF_JSON: { - Object objGetUTXOResponse; + UniValue objGetUTXOResponse(UniValue::VOBJ); // pack in some essentials // use more or less the same output as mentioned in Bip64 @@ -481,15 +500,15 @@ static bool rest_getutxos(AcceptedConnection* conn, objGetUTXOResponse.push_back(Pair("chaintipHash", chainActive.Tip()->GetBlockHash().GetHex())); objGetUTXOResponse.push_back(Pair("bitmap", bitmapStringRepresentation)); - Array utxos; + UniValue utxos(UniValue::VARR); BOOST_FOREACH (const CCoin& coin, outs) { - Object utxo; + UniValue utxo(UniValue::VOBJ); utxo.push_back(Pair("txvers", (int32_t)coin.nTxVer)); utxo.push_back(Pair("height", (int32_t)coin.nHeight)); utxo.push_back(Pair("value", ValueFromAmount(coin.out.nValue))); // include the script in a json output - Object o; + UniValue o(UniValue::VOBJ); ScriptPubKeyToJSON(coin.out.scriptPubKey, o, true); utxo.push_back(Pair("scriptPubKey", o)); utxos.push_back(utxo); @@ -497,7 +516,7 @@ static bool rest_getutxos(AcceptedConnection* conn, objGetUTXOResponse.push_back(Pair("utxos", utxos)); // return json string - string strJSON = write_string(Value(objGetUTXOResponse), false) + "\n"; + string strJSON = objGetUTXOResponse.write() + "\n"; conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush; return true; } diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index ed32ee7723..e45368cb97 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -4,20 +4,21 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "checkpoints.h" +#include "consensus/validation.h" #include "main.h" +#include "primitives/transaction.h" #include "rpcserver.h" #include "sync.h" #include "util.h" #include <stdint.h> -#include "json/json_spirit_value.h" +#include "univalue/univalue.h" -using namespace json_spirit; using namespace std; -extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry); -void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex); +extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); +void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); double GetDifficulty(const CBlockIndex* blockindex) { @@ -51,9 +52,9 @@ double GetDifficulty(const CBlockIndex* blockindex) } -Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false) +UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false) { - Object result; + UniValue result(UniValue::VOBJ); result.push_back(Pair("hash", block.GetHash().GetHex())); int confirmations = -1; // Only report confirmations if the block is on the main chain @@ -64,12 +65,12 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDe result.push_back(Pair("height", blockindex->nHeight)); result.push_back(Pair("version", block.nVersion)); result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); - Array txs; + UniValue txs(UniValue::VARR); BOOST_FOREACH(const CTransaction&tx, block.vtx) { if(txDetails) { - Object objTx; + UniValue objTx(UniValue::VOBJ); TxToJSON(tx, uint256(), objTx); txs.push_back(objTx); } @@ -92,7 +93,7 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDe } -Value getblockcount(const Array& params, bool fHelp) +UniValue getblockcount(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( @@ -109,7 +110,7 @@ Value getblockcount(const Array& params, bool fHelp) return chainActive.Height(); } -Value getbestblockhash(const Array& params, bool fHelp) +UniValue getbestblockhash(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( @@ -126,7 +127,7 @@ Value getbestblockhash(const Array& params, bool fHelp) return chainActive.Tip()->GetBlockHash().GetHex(); } -Value getdifficulty(const Array& params, bool fHelp) +UniValue getdifficulty(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( @@ -144,7 +145,7 @@ Value getdifficulty(const Array& params, bool fHelp) } -Value getrawmempool(const Array& params, bool fHelp) +UniValue getrawmempool(const UniValue& params, bool fHelp) { if (fHelp || params.size() > 1) throw runtime_error( @@ -170,7 +171,7 @@ Value getrawmempool(const Array& params, bool fHelp) " \"transactionid\", (string) parent transaction id\n" " ... ]\n" " }, ...\n" - "]\n" + "}\n" "\nExamples\n" + HelpExampleCli("getrawmempool", "true") + HelpExampleRpc("getrawmempool", "true") @@ -185,12 +186,12 @@ Value getrawmempool(const Array& params, bool fHelp) if (fVerbose) { LOCK(mempool.cs); - Object o; + UniValue o(UniValue::VOBJ); BOOST_FOREACH(const PAIRTYPE(uint256, CTxMemPoolEntry)& entry, mempool.mapTx) { const uint256& hash = entry.first; const CTxMemPoolEntry& e = entry.second; - Object info; + UniValue info(UniValue::VOBJ); info.push_back(Pair("size", (int)e.GetTxSize())); info.push_back(Pair("fee", ValueFromAmount(e.GetFee()))); info.push_back(Pair("time", e.GetTime())); @@ -204,7 +205,13 @@ Value getrawmempool(const Array& params, bool fHelp) if (mempool.exists(txin.prevout.hash)) setDepends.insert(txin.prevout.hash.ToString()); } - Array depends(setDepends.begin(), setDepends.end()); + + UniValue depends(UniValue::VARR); + BOOST_FOREACH(const string& dep, setDepends) + { + depends.push_back(dep); + } + info.push_back(Pair("depends", depends)); o.push_back(Pair(hash.ToString(), info)); } @@ -215,7 +222,7 @@ Value getrawmempool(const Array& params, bool fHelp) vector<uint256> vtxid; mempool.queryHashes(vtxid); - Array a; + UniValue a(UniValue::VARR); BOOST_FOREACH(const uint256& hash, vtxid) a.push_back(hash.ToString()); @@ -223,7 +230,7 @@ Value getrawmempool(const Array& params, bool fHelp) } } -Value getblockhash(const Array& params, bool fHelp) +UniValue getblockhash(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) throw runtime_error( @@ -248,7 +255,7 @@ Value getblockhash(const Array& params, bool fHelp) return pblockindex->GetBlockHash().GetHex(); } -Value getblock(const Array& params, bool fHelp) +UniValue getblock(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( @@ -299,6 +306,9 @@ Value getblock(const Array& params, bool fHelp) CBlock block; CBlockIndex* pblockindex = mapBlockIndex[hash]; + if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); + if(!ReadBlockFromDisk(block, pblockindex)) throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); @@ -313,7 +323,7 @@ Value getblock(const Array& params, bool fHelp) return blockToJSON(block, pblockindex); } -Value gettxoutsetinfo(const Array& params, bool fHelp) +UniValue gettxoutsetinfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( @@ -337,7 +347,7 @@ Value gettxoutsetinfo(const Array& params, bool fHelp) LOCK(cs_main); - Object ret; + UniValue ret(UniValue::VOBJ); CCoinsStats stats; FlushStateToDisk(); @@ -353,7 +363,7 @@ Value gettxoutsetinfo(const Array& params, bool fHelp) return ret; } -Value gettxout(const Array& params, bool fHelp) +UniValue gettxout(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 2 || params.size() > 3) throw runtime_error( @@ -393,7 +403,7 @@ Value gettxout(const Array& params, bool fHelp) LOCK(cs_main); - Object ret; + UniValue ret(UniValue::VOBJ); std::string strHash = params[0].get_str(); uint256 hash(uint256S(strHash)); @@ -407,14 +417,14 @@ Value gettxout(const Array& params, bool fHelp) LOCK(mempool.cs); CCoinsViewMemPool view(pcoinsTip, mempool); if (!view.GetCoins(hash, coins)) - return Value::null; + return NullUniValue; mempool.pruneSpent(hash, coins); // TODO: this should be done by the CCoinsViewMemPool } else { if (!pcoinsTip->GetCoins(hash, coins)) - return Value::null; + return NullUniValue; } if (n<0 || (unsigned int)n>=coins.vout.size() || coins.vout[n].IsNull()) - return Value::null; + return NullUniValue; BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); CBlockIndex *pindex = it->second; @@ -424,7 +434,7 @@ Value gettxout(const Array& params, bool fHelp) else ret.push_back(Pair("confirmations", pindex->nHeight - coins.nHeight + 1)); ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue))); - Object o; + UniValue o(UniValue::VOBJ); ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o, true); ret.push_back(Pair("scriptPubKey", o)); ret.push_back(Pair("version", coins.nVersion)); @@ -433,7 +443,7 @@ Value gettxout(const Array& params, bool fHelp) return ret; } -Value verifychain(const Array& params, bool fHelp) +UniValue verifychain(const UniValue& params, bool fHelp) { if (fHelp || params.size() > 2) throw runtime_error( @@ -461,7 +471,7 @@ Value verifychain(const Array& params, bool fHelp) return CVerifyDB().VerifyDB(pcoinsTip, nCheckLevel, nCheckDepth); } -Value getblockchaininfo(const Array& params, bool fHelp) +UniValue getblockchaininfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( @@ -484,7 +494,7 @@ Value getblockchaininfo(const Array& params, bool fHelp) LOCK(cs_main); - Object obj; + UniValue obj(UniValue::VOBJ); obj.push_back(Pair("chain", Params().NetworkIDString())); obj.push_back(Pair("blocks", (int)chainActive.Height())); obj.push_back(Pair("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1)); @@ -492,6 +502,15 @@ Value getblockchaininfo(const Array& params, bool fHelp) obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("verificationprogress", Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.Tip()))); obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex())); + obj.push_back(Pair("pruned", fPruneMode)); + if (fPruneMode) + { + CBlockIndex *block = chainActive.Tip(); + while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) + block = block->pprev; + + obj.push_back(Pair("pruneheight", block->nHeight)); + } return obj; } @@ -510,7 +529,7 @@ struct CompareBlocksByHeight } }; -Value getchaintips(const Array& params, bool fHelp) +UniValue getchaintips(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( @@ -562,10 +581,10 @@ Value getchaintips(const Array& params, bool fHelp) setTips.insert(chainActive.Tip()); /* Construct the output array. */ - Array res; + UniValue res(UniValue::VARR); BOOST_FOREACH(const CBlockIndex* block, setTips) { - Object obj; + UniValue obj(UniValue::VOBJ); obj.push_back(Pair("height", block->nHeight)); obj.push_back(Pair("hash", block->phashBlock->GetHex())); @@ -600,7 +619,7 @@ Value getchaintips(const Array& params, bool fHelp) return res; } -Value getmempoolinfo(const Array& params, bool fHelp) +UniValue getmempoolinfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( @@ -616,14 +635,14 @@ Value getmempoolinfo(const Array& params, bool fHelp) + HelpExampleRpc("getmempoolinfo", "") ); - Object ret; + UniValue ret(UniValue::VOBJ); ret.push_back(Pair("size", (int64_t) mempool.size())); ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize())); return ret; } -Value invalidateblock(const Array& params, bool fHelp) +UniValue invalidateblock(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) throw runtime_error( @@ -658,10 +677,10 @@ Value invalidateblock(const Array& params, bool fHelp) throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); } - return Value::null; + return NullUniValue; } -Value reconsiderblock(const Array& params, bool fHelp) +UniValue reconsiderblock(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) throw runtime_error( @@ -697,5 +716,5 @@ Value reconsiderblock(const Array& params, bool fHelp) throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); } - return Value::null; + return NullUniValue; } diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index 4b576b3707..f254da5de0 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -11,8 +11,10 @@ #include <set> #include <stdint.h> +#include <boost/algorithm/string/case_conv.hpp> // for to_lower() +#include "univalue/univalue.h" + using namespace std; -using namespace json_spirit; class CRPCConvertParam { @@ -119,25 +121,32 @@ CRPCConvertTable::CRPCConvertTable() static CRPCConvertTable rpcCvtTable; +/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null) + * as well as objects and arrays. + */ +UniValue ParseNonRFCJSONValue(const std::string& strVal) +{ + UniValue jVal; + if (!jVal.read(std::string("[")+strVal+std::string("]")) || + !jVal.isArray() || jVal.size()!=1) + throw runtime_error(string("Error parsing JSON:")+strVal); + return jVal[0]; +} + /** Convert strings to command-specific RPC representation */ -Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams) +UniValue RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams) { - Array params; + UniValue params(UniValue::VARR); for (unsigned int idx = 0; idx < strParams.size(); idx++) { const std::string& strVal = strParams[idx]; - // insert string value directly if (!rpcCvtTable.convert(strMethod, idx)) { + // insert string value directly params.push_back(strVal); - } - - // parse string as JSON, insert bool/number/object/etc. value - else { - Value jVal; - if (!read_string(strVal, jVal)) - throw runtime_error(string("Error parsing JSON:")+strVal); - params.push_back(jVal); + } else { + // parse string as JSON, insert bool/number/object/etc. value + params.push_back(ParseNonRFCJSONValue(strVal)); } } diff --git a/src/rpcclient.h b/src/rpcclient.h index 42fa2d06fe..d68b4ed6ae 100644 --- a/src/rpcclient.h +++ b/src/rpcclient.h @@ -6,10 +6,12 @@ #ifndef BITCOIN_RPCCLIENT_H #define BITCOIN_RPCCLIENT_H -#include "json/json_spirit_reader_template.h" -#include "json/json_spirit_utils.h" -#include "json/json_spirit_writer_template.h" +#include "univalue/univalue.h" -json_spirit::Array RPCConvertValues(const std::string& strMethod, const std::vector<std::string>& strParams); +UniValue RPCConvertValues(const std::string& strMethod, const std::vector<std::string>& strParams); +/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null) + * as well as objects and arrays. + */ +UniValue ParseNonRFCJSONValue(const std::string& strVal); #endif // BITCOIN_RPCCLIENT_H diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 24b865150a..f332814611 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -6,6 +6,7 @@ #include "amount.h" #include "chainparams.h" #include "consensus/consensus.h" +#include "consensus/validation.h" #include "core_io.h" #include "init.h" #include "main.h" @@ -16,7 +17,6 @@ #include "util.h" #include "validationinterface.h" #ifdef ENABLE_WALLET -#include "wallet/db.h" #include "wallet/wallet.h" #endif @@ -24,10 +24,8 @@ #include <boost/assign/list_of.hpp> -#include "json/json_spirit_utils.h" -#include "json/json_spirit_value.h" +#include "univalue/univalue.h" -using namespace json_spirit; using namespace std; /** @@ -35,7 +33,7 @@ using namespace std; * or from the last difficulty change if 'lookup' is nonpositive. * If 'height' is nonnegative, compute the estimate at the time when a given block was found. */ -Value GetNetworkHashPS(int lookup, int height) { +UniValue GetNetworkHashPS(int lookup, int height) { CBlockIndex *pb = chainActive.Tip(); if (height >= 0 && height < chainActive.Height()) @@ -72,7 +70,7 @@ Value GetNetworkHashPS(int lookup, int height) { return (int64_t)(workDiff.getdouble() / timeDiff); } -Value getnetworkhashps(const Array& params, bool fHelp) +UniValue getnetworkhashps(const UniValue& params, bool fHelp) { if (fHelp || params.size() > 2) throw runtime_error( @@ -95,7 +93,7 @@ Value getnetworkhashps(const Array& params, bool fHelp) } #ifdef ENABLE_WALLET -Value getgenerate(const Array& params, bool fHelp) +UniValue getgenerate(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( @@ -114,7 +112,7 @@ Value getgenerate(const Array& params, bool fHelp) return GetBoolArg("-gen", false); } -Value generate(const Array& params, bool fHelp) +UniValue generate(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 1) throw runtime_error( @@ -147,7 +145,7 @@ Value generate(const Array& params, bool fHelp) nHeightEnd = nHeightStart+nGenerate; } unsigned int nExtraNonce = 0; - Array blockHashes; + UniValue blockHashes(UniValue::VARR); while (nHeight < nHeightEnd) { auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey)); @@ -164,7 +162,7 @@ Value generate(const Array& params, bool fHelp) ++pblock->nNonce; } CValidationState state; - if (!ProcessNewBlock(state, NULL, pblock)) + if (!ProcessNewBlock(state, NULL, pblock, true, NULL)) throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted"); ++nHeight; blockHashes.push_back(pblock->GetHash().GetHex()); @@ -173,7 +171,7 @@ Value generate(const Array& params, bool fHelp) } -Value setgenerate(const Array& params, bool fHelp) +UniValue setgenerate(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( @@ -216,12 +214,12 @@ Value setgenerate(const Array& params, bool fHelp) mapArgs ["-genproclimit"] = itostr(nGenProcLimit); GenerateBitcoins(fGenerate, pwalletMain, nGenProcLimit); - return Value::null; + return NullUniValue; } #endif -Value getmininginfo(const Array& params, bool fHelp) +UniValue getmininginfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( @@ -248,7 +246,7 @@ Value getmininginfo(const Array& params, bool fHelp) LOCK(cs_main); - Object obj; + UniValue obj(UniValue::VOBJ); obj.push_back(Pair("blocks", (int)chainActive.Height())); obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize)); obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx)); @@ -267,7 +265,7 @@ Value getmininginfo(const Array& params, bool fHelp) // NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts -Value prioritisetransaction(const Array& params, bool fHelp) +UniValue prioritisetransaction(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 3) throw runtime_error( @@ -299,10 +297,10 @@ Value prioritisetransaction(const Array& params, bool fHelp) // NOTE: Assumes a conclusive result; if result is inconclusive, it must be handled by caller -static Value BIP22ValidationResult(const CValidationState& state) +static UniValue BIP22ValidationResult(const CValidationState& state) { if (state.IsValid()) - return Value::null; + return NullUniValue; std::string strRejectReason = state.GetRejectReason(); if (state.IsError()) @@ -317,7 +315,7 @@ static Value BIP22ValidationResult(const CValidationState& state) return "valid?"; } -Value getblocktemplate(const Array& params, bool fHelp) +UniValue getblocktemplate(const UniValue& params, bool fHelp) { if (fHelp || params.size() > 1) throw runtime_error( @@ -382,14 +380,14 @@ Value getblocktemplate(const Array& params, bool fHelp) LOCK(cs_main); std::string strMode = "template"; - Value lpval = Value::null; + UniValue lpval = NullUniValue; if (params.size() > 0) { - const Object& oparam = params[0].get_obj(); - const Value& modeval = find_value(oparam, "mode"); - if (modeval.type() == str_type) + const UniValue& oparam = params[0].get_obj(); + const UniValue& modeval = find_value(oparam, "mode"); + if (modeval.isStr()) strMode = modeval.get_str(); - else if (modeval.type() == null_type) + else if (modeval.isNull()) { /* Do nothing */ } @@ -399,8 +397,8 @@ Value getblocktemplate(const Array& params, bool fHelp) if (strMode == "proposal") { - const Value& dataval = find_value(oparam, "data"); - if (dataval.type() != str_type) + const UniValue& dataval = find_value(oparam, "data"); + if (!dataval.isStr()) throw JSONRPCError(RPC_TYPE_ERROR, "Missing data String key for proposal"); CBlock block; @@ -439,14 +437,14 @@ Value getblocktemplate(const Array& params, bool fHelp) static unsigned int nTransactionsUpdatedLast; - if (lpval.type() != null_type) + if (!lpval.isNull()) { // Wait to respond until either the best block changes, OR a minute has passed and there are more transactions uint256 hashWatchedChain; boost::system_time checktxtime; unsigned int nTransactionsUpdatedLastLP; - if (lpval.type() == str_type) + if (lpval.isStr()) { // Format: <hashBestChain><nTransactionsUpdatedLast> std::string lpstr = lpval.get_str(); @@ -520,26 +518,25 @@ Value getblocktemplate(const Array& params, bool fHelp) UpdateTime(pblock, Params().GetConsensus(), pindexPrev); pblock->nNonce = 0; - static const Array aCaps = boost::assign::list_of("proposal"); + UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal"); - Array transactions; + UniValue transactions(UniValue::VARR); map<uint256, int64_t> setTxIndex; int i = 0; - BOOST_FOREACH (CTransaction& tx, pblock->vtx) - { + BOOST_FOREACH (const CTransaction& tx, pblock->vtx) { uint256 txHash = tx.GetHash(); setTxIndex[txHash] = i++; if (tx.IsCoinBase()) continue; - Object entry; + UniValue entry(UniValue::VOBJ); entry.push_back(Pair("data", EncodeHexTx(tx))); entry.push_back(Pair("hash", txHash.GetHex())); - Array deps; + UniValue deps(UniValue::VARR); BOOST_FOREACH (const CTxIn &in, tx.vin) { if (setTxIndex.count(in.prevout.hash)) @@ -554,12 +551,12 @@ Value getblocktemplate(const Array& params, bool fHelp) transactions.push_back(entry); } - Object aux; + UniValue aux(UniValue::VOBJ); aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); - static Array aMutable; + static UniValue aMutable(UniValue::VARR); if (aMutable.empty()) { aMutable.push_back("time"); @@ -567,7 +564,7 @@ Value getblocktemplate(const Array& params, bool fHelp) aMutable.push_back("prevblock"); } - Object result; + UniValue result(UniValue::VOBJ); result.push_back(Pair("capabilities", aCaps)); result.push_back(Pair("version", pblock->nVersion)); result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); @@ -606,7 +603,7 @@ protected: }; }; -Value submitblock(const Array& params, bool fHelp) +UniValue submitblock(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( @@ -650,7 +647,7 @@ Value submitblock(const Array& params, bool fHelp) CValidationState state; submitblock_StateCatcher sc(block.GetHash()); RegisterValidationInterface(&sc); - bool fAccepted = ProcessNewBlock(state, NULL, &block); + bool fAccepted = ProcessNewBlock(state, NULL, &block, true, NULL); UnregisterValidationInterface(&sc); if (fBlockPresent) { @@ -667,7 +664,7 @@ Value submitblock(const Array& params, bool fHelp) return BIP22ValidationResult(state); } -Value estimatefee(const Array& params, bool fHelp) +UniValue estimatefee(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) throw runtime_error( @@ -686,7 +683,7 @@ Value estimatefee(const Array& params, bool fHelp) + HelpExampleCli("estimatefee", "6") ); - RPCTypeCheck(params, boost::assign::list_of(int_type)); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); int nBlocks = params[0].get_int(); if (nBlocks < 1) @@ -699,7 +696,7 @@ Value estimatefee(const Array& params, bool fHelp) return ValueFromAmount(feeRate.GetFeePerK()); } -Value estimatepriority(const Array& params, bool fHelp) +UniValue estimatepriority(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) throw runtime_error( @@ -718,7 +715,7 @@ Value estimatepriority(const Array& params, bool fHelp) + HelpExampleCli("estimatepriority", "6") ); - RPCTypeCheck(params, boost::assign::list_of(int_type)); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); int nBlocks = params[0].get_int(); if (nBlocks < 1) diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index f5bef2a077..fe6eb81b84 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -20,10 +20,9 @@ #include <stdint.h> #include <boost/assign/list_of.hpp> -#include "json/json_spirit_utils.h" -#include "json/json_spirit_value.h" -using namespace json_spirit; +#include "univalue/univalue.h" + using namespace std; /** @@ -39,7 +38,7 @@ using namespace std; * * Or alternatively, create a specific query method for the information. **/ -Value getinfo(const Array& params, bool fHelp) +UniValue getinfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( @@ -78,7 +77,7 @@ Value getinfo(const Array& params, bool fHelp) proxyType proxy; GetProxy(NET_IPV4, proxy); - Object obj; + UniValue obj(UniValue::VOBJ); obj.push_back(Pair("version", CLIENT_VERSION)); obj.push_back(Pair("protocolversion", PROTOCOL_VERSION)); #ifdef ENABLE_WALLET @@ -108,7 +107,7 @@ Value getinfo(const Array& params, bool fHelp) } #ifdef ENABLE_WALLET -class DescribeAddressVisitor : public boost::static_visitor<Object> +class DescribeAddressVisitor : public boost::static_visitor<UniValue> { private: isminetype mine; @@ -116,10 +115,10 @@ private: public: DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {} - Object operator()(const CNoDestination &dest) const { return Object(); } + UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); } - Object operator()(const CKeyID &keyID) const { - Object obj; + UniValue operator()(const CKeyID &keyID) const { + UniValue obj(UniValue::VOBJ); CPubKey vchPubKey; obj.push_back(Pair("isscript", false)); if (mine == ISMINE_SPENDABLE) { @@ -130,8 +129,8 @@ public: return obj; } - Object operator()(const CScriptID &scriptID) const { - Object obj; + UniValue operator()(const CScriptID &scriptID) const { + UniValue obj(UniValue::VOBJ); obj.push_back(Pair("isscript", true)); if (mine != ISMINE_NO) { CScript subscript; @@ -142,7 +141,7 @@ public: ExtractDestinations(subscript, whichType, addresses, nRequired); obj.push_back(Pair("script", GetTxnOutputType(whichType))); obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); - Array a; + UniValue a(UniValue::VARR); BOOST_FOREACH(const CTxDestination& addr, addresses) a.push_back(CBitcoinAddress(addr).ToString()); obj.push_back(Pair("addresses", a)); @@ -154,7 +153,7 @@ public: }; #endif -Value validateaddress(const Array& params, bool fHelp) +UniValue validateaddress(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) throw runtime_error( @@ -187,7 +186,7 @@ Value validateaddress(const Array& params, bool fHelp) CBitcoinAddress address(params[0].get_str()); bool isValid = address.IsValid(); - Object ret; + UniValue ret(UniValue::VOBJ); ret.push_back(Pair("isvalid", isValid)); if (isValid) { @@ -203,8 +202,8 @@ Value validateaddress(const Array& params, bool fHelp) ret.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false)); if (mine != ISMINE_NO) { ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false)); - Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest); - ret.insert(ret.end(), detail.begin(), detail.end()); + UniValue detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest); + ret.pushKVs(detail); } if (pwalletMain && pwalletMain->mapAddressBook.count(dest)) ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest].name)); @@ -216,10 +215,10 @@ Value validateaddress(const Array& params, bool fHelp) /** * Used by addmultisigaddress / createmultisig: */ -CScript _createmultisig_redeemScript(const Array& params) +CScript _createmultisig_redeemScript(const UniValue& params) { int nRequired = params[0].get_int(); - const Array& keys = params[1].get_array(); + const UniValue& keys = params[1].get_array(); // Gather public keys if (nRequired < 1) @@ -277,7 +276,7 @@ CScript _createmultisig_redeemScript(const Array& params) return result; } -Value createmultisig(const Array& params, bool fHelp) +UniValue createmultisig(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 2 || params.size() > 2) { @@ -313,14 +312,14 @@ Value createmultisig(const Array& params, bool fHelp) CScriptID innerID(inner); CBitcoinAddress address(innerID); - Object result; + UniValue result(UniValue::VOBJ); result.push_back(Pair("address", address.ToString())); result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end()))); return result; } -Value verifymessage(const Array& params, bool fHelp) +UniValue verifymessage(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 3) throw runtime_error( @@ -374,7 +373,7 @@ Value verifymessage(const Array& params, bool fHelp) return (pubkey.GetID() == keyID); } -Value setmocktime(const Array& params, bool fHelp) +UniValue setmocktime(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) throw runtime_error( @@ -390,8 +389,8 @@ Value setmocktime(const Array& params, bool fHelp) LOCK(cs_main); - RPCTypeCheck(params, boost::assign::list_of(int_type)); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); SetMockTime(params[0].get_int64()); - return Value::null; + return NullUniValue; } diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index bdee5b9f2e..a36831de2a 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -16,12 +16,11 @@ #include <boost/foreach.hpp> -#include "json/json_spirit_value.h" +#include "univalue/univalue.h" -using namespace json_spirit; using namespace std; -Value getconnectioncount(const Array& params, bool fHelp) +UniValue getconnectioncount(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( @@ -39,7 +38,7 @@ Value getconnectioncount(const Array& params, bool fHelp) return (int)vNodes.size(); } -Value ping(const Array& params, bool fHelp) +UniValue ping(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( @@ -59,7 +58,7 @@ Value ping(const Array& params, bool fHelp) pNode->fPingQueued = true; } - return Value::null; + return NullUniValue; } static void CopyNodeStats(std::vector<CNodeStats>& vstats) @@ -75,7 +74,7 @@ static void CopyNodeStats(std::vector<CNodeStats>& vstats) } } -Value getpeerinfo(const Array& params, bool fHelp) +UniValue getpeerinfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( @@ -120,10 +119,10 @@ Value getpeerinfo(const Array& params, bool fHelp) vector<CNodeStats> vstats; CopyNodeStats(vstats); - Array ret; + UniValue ret(UniValue::VARR); BOOST_FOREACH(const CNodeStats& stats, vstats) { - Object obj; + UniValue obj(UniValue::VOBJ); CNodeStateStats statestats; bool fStateStats = GetNodeStateStats(stats.nodeid, statestats); obj.push_back(Pair("id", stats.nodeid)); @@ -151,7 +150,7 @@ Value getpeerinfo(const Array& params, bool fHelp) obj.push_back(Pair("banscore", statestats.nMisbehavior)); obj.push_back(Pair("synced_headers", statestats.nSyncHeight)); obj.push_back(Pair("synced_blocks", statestats.nCommonHeight)); - Array heights; + UniValue heights(UniValue::VARR); BOOST_FOREACH(int height, statestats.vHeightInFlight) { heights.push_back(height); } @@ -165,7 +164,7 @@ Value getpeerinfo(const Array& params, bool fHelp) return ret; } -Value addnode(const Array& params, bool fHelp) +UniValue addnode(const UniValue& params, bool fHelp) { string strCommand; if (params.size() == 2) @@ -190,7 +189,7 @@ Value addnode(const Array& params, bool fHelp) { CAddress addr; OpenNetworkConnection(addr, NULL, strNode.c_str()); - return Value::null; + return NullUniValue; } LOCK(cs_vAddedNodes); @@ -212,10 +211,10 @@ Value addnode(const Array& params, bool fHelp) vAddedNodes.erase(it); } - return Value::null; + return NullUniValue; } -Value getaddednodeinfo(const Array& params, bool fHelp) +UniValue getaddednodeinfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( @@ -254,29 +253,29 @@ Value getaddednodeinfo(const Array& params, bool fHelp) if (params.size() == 1) { LOCK(cs_vAddedNodes); - BOOST_FOREACH(string& strAddNode, vAddedNodes) + BOOST_FOREACH(const std::string& strAddNode, vAddedNodes) laddedNodes.push_back(strAddNode); } else { string strNode = params[1].get_str(); LOCK(cs_vAddedNodes); - BOOST_FOREACH(string& strAddNode, vAddedNodes) + BOOST_FOREACH(const std::string& strAddNode, vAddedNodes) { if (strAddNode == strNode) { laddedNodes.push_back(strAddNode); break; } + } if (laddedNodes.size() == 0) throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added."); } - Array ret; + UniValue ret(UniValue::VARR); if (!fDns) { - BOOST_FOREACH(string& strAddNode, laddedNodes) - { - Object obj; + BOOST_FOREACH (const std::string& strAddNode, laddedNodes) { + UniValue obj(UniValue::VOBJ); obj.push_back(Pair("addednode", strAddNode)); ret.push_back(obj); } @@ -284,17 +283,16 @@ Value getaddednodeinfo(const Array& params, bool fHelp) } list<pair<string, vector<CService> > > laddedAddreses(0); - BOOST_FOREACH(string& strAddNode, laddedNodes) - { + BOOST_FOREACH(const std::string& strAddNode, laddedNodes) { vector<CService> vservNode(0); if(Lookup(strAddNode.c_str(), vservNode, Params().GetDefaultPort(), fNameLookup, 0)) laddedAddreses.push_back(make_pair(strAddNode, vservNode)); else { - Object obj; + UniValue obj(UniValue::VOBJ); obj.push_back(Pair("addednode", strAddNode)); obj.push_back(Pair("connected", false)); - Array addresses; + UniValue addresses(UniValue::VARR); obj.push_back(Pair("addresses", addresses)); } } @@ -302,17 +300,16 @@ Value getaddednodeinfo(const Array& params, bool fHelp) LOCK(cs_vNodes); for (list<pair<string, vector<CService> > >::iterator it = laddedAddreses.begin(); it != laddedAddreses.end(); it++) { - Object obj; + UniValue obj(UniValue::VOBJ); obj.push_back(Pair("addednode", it->first)); - Array addresses; + UniValue addresses(UniValue::VARR); bool fConnected = false; - BOOST_FOREACH(CService& addrNode, it->second) - { + BOOST_FOREACH(const CService& addrNode, it->second) { bool fFound = false; - Object node; + UniValue node(UniValue::VOBJ); node.push_back(Pair("address", addrNode.ToString())); - BOOST_FOREACH(CNode* pnode, vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) { if (pnode->addr == addrNode) { fFound = true; @@ -320,6 +317,7 @@ Value getaddednodeinfo(const Array& params, bool fHelp) node.push_back(Pair("connected", pnode->fInbound ? "inbound" : "outbound")); break; } + } if (!fFound) node.push_back(Pair("connected", "false")); addresses.push_back(node); @@ -332,7 +330,7 @@ Value getaddednodeinfo(const Array& params, bool fHelp) return ret; } -Value getnettotals(const Array& params, bool fHelp) +UniValue getnettotals(const UniValue& params, bool fHelp) { if (fHelp || params.size() > 0) throw runtime_error( @@ -350,23 +348,23 @@ Value getnettotals(const Array& params, bool fHelp) + HelpExampleRpc("getnettotals", "") ); - Object obj; + UniValue obj(UniValue::VOBJ); obj.push_back(Pair("totalbytesrecv", CNode::GetTotalBytesRecv())); obj.push_back(Pair("totalbytessent", CNode::GetTotalBytesSent())); obj.push_back(Pair("timemillis", GetTimeMillis())); return obj; } -static Array GetNetworksInfo() +static UniValue GetNetworksInfo() { - Array networks; + UniValue networks(UniValue::VARR); for(int n=0; n<NET_MAX; ++n) { enum Network network = static_cast<enum Network>(n); if(network == NET_UNROUTABLE) continue; proxyType proxy; - Object obj; + UniValue obj(UniValue::VOBJ); GetProxy(network, proxy); obj.push_back(Pair("name", GetNetworkName(network))); obj.push_back(Pair("limited", IsLimited(network))); @@ -378,7 +376,7 @@ static Array GetNetworksInfo() return networks; } -Value getnetworkinfo(const Array& params, bool fHelp) +UniValue getnetworkinfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( @@ -418,7 +416,7 @@ Value getnetworkinfo(const Array& params, bool fHelp) LOCK(cs_main); - Object obj; + UniValue obj(UniValue::VOBJ); obj.push_back(Pair("version", CLIENT_VERSION)); obj.push_back(Pair("subversion", FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<string>()))); @@ -428,12 +426,12 @@ Value getnetworkinfo(const Array& params, bool fHelp) obj.push_back(Pair("connections", (int)vNodes.size())); obj.push_back(Pair("networks", GetNetworksInfo())); obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); - Array localAddresses; + UniValue localAddresses(UniValue::VARR); { LOCK(cs_mapLocalHost); BOOST_FOREACH(const PAIRTYPE(CNetAddr, LocalServiceInfo) &item, mapLocalHost) { - Object rec; + UniValue rec(UniValue::VOBJ); rec.push_back(Pair("address", item.first.ToString())); rec.push_back(Pair("port", item.second.nPort)); rec.push_back(Pair("score", item.second.nScore)); diff --git a/src/rpcprotocol.cpp b/src/rpcprotocol.cpp index 95d6b9e531..090e5ea7f4 100644 --- a/src/rpcprotocol.cpp +++ b/src/rpcprotocol.cpp @@ -23,10 +23,10 @@ #include <boost/iostreams/concepts.hpp> #include <boost/iostreams/stream.hpp> #include <boost/shared_ptr.hpp> -#include "json/json_spirit_writer_template.h" + +#include "univalue/univalue.h" using namespace std; -using namespace json_spirit; //! Number of bytes to allocate and read at most at once in post data const size_t POST_READ_SIZE = 256 * 1024; @@ -254,20 +254,20 @@ int ReadHTTPMessage(std::basic_istream<char>& stream, map<string, * http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx */ -string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id) +string JSONRPCRequest(const string& strMethod, const UniValue& params, const UniValue& id) { - Object request; + UniValue request(UniValue::VOBJ); request.push_back(Pair("method", strMethod)); request.push_back(Pair("params", params)); request.push_back(Pair("id", id)); - return write_string(Value(request), false) + "\n"; + return request.write() + "\n"; } -Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id) +UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id) { - Object reply; - if (error.type() != null_type) - reply.push_back(Pair("result", Value::null)); + UniValue reply(UniValue::VOBJ); + if (!error.isNull()) + reply.push_back(Pair("result", NullUniValue)); else reply.push_back(Pair("result", result)); reply.push_back(Pair("error", error)); @@ -275,15 +275,15 @@ Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id) return reply; } -string JSONRPCReply(const Value& result, const Value& error, const Value& id) +string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id) { - Object reply = JSONRPCReplyObj(result, error, id); - return write_string(Value(reply), false) + "\n"; + UniValue reply = JSONRPCReplyObj(result, error, id); + return reply.write() + "\n"; } -Object JSONRPCError(int code, const string& message) +UniValue JSONRPCError(int code, const string& message) { - Object error; + UniValue error(UniValue::VOBJ); error.push_back(Pair("code", code)); error.push_back(Pair("message", message)); return error; diff --git a/src/rpcprotocol.h b/src/rpcprotocol.h index 4f3f70fb37..b9fa091955 100644 --- a/src/rpcprotocol.h +++ b/src/rpcprotocol.h @@ -15,9 +15,7 @@ #include <boost/asio.hpp> #include <boost/asio/ssl.hpp> -#include "json/json_spirit_reader_template.h" -#include "json/json_spirit_utils.h" -#include "json/json_spirit_writer_template.h" +#include "univalue/univalue.h" //! HTTP status codes enum HTTPStatusCode @@ -160,9 +158,9 @@ int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto); int ReadHTTPHeaders(std::basic_istream<char>& stream, std::map<std::string, std::string>& mapHeadersRet); int ReadHTTPMessage(std::basic_istream<char>& stream, std::map<std::string, std::string>& mapHeadersRet, std::string& strMessageRet, int nProto, size_t max_size); -std::string JSONRPCRequest(const std::string& strMethod, const json_spirit::Array& params, const json_spirit::Value& id); -json_spirit::Object JSONRPCReplyObj(const json_spirit::Value& result, const json_spirit::Value& error, const json_spirit::Value& id); -std::string JSONRPCReply(const json_spirit::Value& result, const json_spirit::Value& error, const json_spirit::Value& id); -json_spirit::Object JSONRPCError(int code, const std::string& message); +std::string JSONRPCRequest(const std::string& strMethod, const UniValue& params, const UniValue& id); +UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id); +std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id); +UniValue JSONRPCError(int code, const std::string& message); #endif // BITCOIN_RPCPROTOCOL_H diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index e84d2693a2..20394fc2c1 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -4,13 +4,14 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "base58.h" -#include "primitives/transaction.h" +#include "consensus/validation.h" #include "core_io.h" #include "init.h" #include "keystore.h" #include "main.h" #include "merkleblock.h" #include "net.h" +#include "primitives/transaction.h" #include "rpcserver.h" #include "script/script.h" #include "script/script_error.h" @@ -24,13 +25,12 @@ #include <stdint.h> #include <boost/assign/list_of.hpp> -#include "json/json_spirit_utils.h" -#include "json/json_spirit_value.h" -using namespace json_spirit; +#include "univalue/univalue.h" + using namespace std; -void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex) +void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex) { txnouttype type; vector<CTxDestination> addresses; @@ -48,26 +48,26 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeH out.push_back(Pair("reqSigs", nRequired)); out.push_back(Pair("type", GetTxnOutputType(type))); - Array a; + UniValue a(UniValue::VARR); BOOST_FOREACH(const CTxDestination& addr, addresses) a.push_back(CBitcoinAddress(addr).ToString()); out.push_back(Pair("addresses", a)); } -void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) +void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) { entry.push_back(Pair("txid", tx.GetHash().GetHex())); entry.push_back(Pair("version", tx.nVersion)); entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); - Array vin; + UniValue vin(UniValue::VARR); BOOST_FOREACH(const CTxIn& txin, tx.vin) { - Object in; + UniValue in(UniValue::VOBJ); if (tx.IsCoinBase()) in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); else { in.push_back(Pair("txid", txin.prevout.hash.GetHex())); in.push_back(Pair("vout", (int64_t)txin.prevout.n)); - Object o; + UniValue o(UniValue::VOBJ); o.push_back(Pair("asm", txin.scriptSig.ToString())); o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); in.push_back(Pair("scriptSig", o)); @@ -76,13 +76,13 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) vin.push_back(in); } entry.push_back(Pair("vin", vin)); - Array vout; + UniValue vout(UniValue::VARR); for (unsigned int i = 0; i < tx.vout.size(); i++) { const CTxOut& txout = tx.vout[i]; - Object out; + UniValue out(UniValue::VOBJ); out.push_back(Pair("value", ValueFromAmount(txout.nValue))); out.push_back(Pair("n", (int64_t)i)); - Object o; + UniValue o(UniValue::VOBJ); ScriptPubKeyToJSON(txout.scriptPubKey, o, true); out.push_back(Pair("scriptPubKey", o)); vout.push_back(out); @@ -105,7 +105,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) } } -Value getrawtransaction(const Array& params, bool fHelp) +UniValue getrawtransaction(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( @@ -189,13 +189,13 @@ Value getrawtransaction(const Array& params, bool fHelp) if (!fVerbose) return strHex; - Object result; + UniValue result(UniValue::VOBJ); result.push_back(Pair("hex", strHex)); TxToJSON(tx, hashBlock, result); return result; } -Value gettxoutproof(const Array& params, bool fHelp) +UniValue gettxoutproof(const UniValue& params, bool fHelp) { if (fHelp || (params.size() != 1 && params.size() != 2)) throw runtime_error( @@ -219,8 +219,9 @@ Value gettxoutproof(const Array& params, bool fHelp) set<uint256> setTxids; uint256 oneTxid; - Array txids = params[0].get_array(); - BOOST_FOREACH(Value& txid, txids) { + UniValue txids = params[0].get_array(); + for (unsigned int idx = 0; idx < txids.size(); idx++) { + const UniValue& txid = txids[idx]; if (txid.get_str().length() != 64 || !IsHex(txid.get_str())) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid txid ")+txid.get_str()); uint256 hash(uint256S(txid.get_str())); @@ -275,7 +276,7 @@ Value gettxoutproof(const Array& params, bool fHelp) return strHex; } -Value verifytxoutproof(const Array& params, bool fHelp) +UniValue verifytxoutproof(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) throw runtime_error( @@ -292,7 +293,7 @@ Value verifytxoutproof(const Array& params, bool fHelp) CMerkleBlock merkleBlock; ssMB >> merkleBlock; - Array res; + UniValue res(UniValue::VARR); vector<uint256> vMatch; if (merkleBlock.txn.ExtractMatches(vMatch) != merkleBlock.header.hashMerkleRoot) @@ -308,7 +309,7 @@ Value verifytxoutproof(const Array& params, bool fHelp) return res; } -Value createrawtransaction(const Array& params, bool fHelp) +UniValue createrawtransaction(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 2) throw runtime_error( @@ -342,20 +343,21 @@ Value createrawtransaction(const Array& params, bool fHelp) ); LOCK(cs_main); - RPCTypeCheck(params, boost::assign::list_of(array_type)(obj_type)); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ)); - Array inputs = params[0].get_array(); - Object sendTo = params[1].get_obj(); + UniValue inputs = params[0].get_array(); + UniValue sendTo = params[1].get_obj(); CMutableTransaction rawTx; - BOOST_FOREACH(const Value& input, inputs) { - const Object& o = input.get_obj(); + for (unsigned int idx = 0; idx < inputs.size(); idx++) { + const UniValue& input = inputs[idx]; + const UniValue& o = input.get_obj(); uint256 txid = ParseHashO(o, "txid"); - const Value& vout_v = find_value(o, "vout"); - if (vout_v.type() != int_type) + const UniValue& vout_v = find_value(o, "vout"); + if (!vout_v.isNum()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); int nOutput = vout_v.get_int(); if (nOutput < 0) @@ -366,17 +368,18 @@ Value createrawtransaction(const Array& params, bool fHelp) } set<CBitcoinAddress> setAddress; - BOOST_FOREACH(const Pair& s, sendTo) { - CBitcoinAddress address(s.name_); + vector<string> addrList = sendTo.getKeys(); + BOOST_FOREACH(const string& name_, addrList) { + CBitcoinAddress address(name_); if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+name_); if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_); + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); setAddress.insert(address); CScript scriptPubKey = GetScriptForDestination(address.Get()); - CAmount nAmount = AmountFromValue(s.value_); + CAmount nAmount = AmountFromValue(sendTo[name_]); CTxOut out(nAmount, scriptPubKey); rawTx.vout.push_back(out); @@ -385,7 +388,7 @@ Value createrawtransaction(const Array& params, bool fHelp) return EncodeHexTx(rawTx); } -Value decoderawtransaction(const Array& params, bool fHelp) +UniValue decoderawtransaction(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) throw runtime_error( @@ -437,20 +440,20 @@ Value decoderawtransaction(const Array& params, bool fHelp) ); LOCK(cs_main); - RPCTypeCheck(params, boost::assign::list_of(str_type)); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); CTransaction tx; if (!DecodeHexTx(tx, params[0].get_str())) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); - Object result; + UniValue result(UniValue::VOBJ); TxToJSON(tx, uint256(), result); return result; } -Value decodescript(const Array& params, bool fHelp) +UniValue decodescript(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) throw runtime_error( @@ -476,9 +479,9 @@ Value decodescript(const Array& params, bool fHelp) ); LOCK(cs_main); - RPCTypeCheck(params, boost::assign::list_of(str_type)); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); - Object r; + UniValue r(UniValue::VOBJ); CScript script; if (params[0].get_str().size() > 0){ vector<unsigned char> scriptData(ParseHexV(params[0], "argument")); @@ -493,9 +496,9 @@ Value decodescript(const Array& params, bool fHelp) } /** Pushes a JSON object for script verification or signing errors to vErrorsRet. */ -static void TxInErrorToJSON(const CTxIn& txin, Array& vErrorsRet, const std::string& strMessage) +static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage) { - Object entry; + UniValue entry(UniValue::VOBJ); entry.push_back(Pair("txid", txin.prevout.hash.ToString())); entry.push_back(Pair("vout", (uint64_t)txin.prevout.n)); entry.push_back(Pair("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); @@ -504,7 +507,7 @@ static void TxInErrorToJSON(const CTxIn& txin, Array& vErrorsRet, const std::str vErrorsRet.push_back(entry); } -Value signrawtransaction(const Array& params, bool fHelp) +UniValue signrawtransaction(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 4) throw runtime_error( @@ -569,7 +572,7 @@ Value signrawtransaction(const Array& params, bool fHelp) #else LOCK(cs_main); #endif - RPCTypeCheck(params, boost::assign::list_of(str_type)(array_type)(array_type)(str_type), true); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VARR)(UniValue::VSTR), true); vector<unsigned char> txData(ParseHexV(params[0], "argument 1")); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); @@ -612,10 +615,11 @@ Value signrawtransaction(const Array& params, bool fHelp) bool fGivenKeys = false; CBasicKeyStore tempKeystore; - if (params.size() > 2 && params[2].type() != null_type) { + if (params.size() > 2 && !params[2].isNull()) { fGivenKeys = true; - Array keys = params[2].get_array(); - BOOST_FOREACH(Value k, keys) { + UniValue keys = params[2].get_array(); + for (unsigned int idx = 0; idx < keys.size(); idx++) { + UniValue k = keys[idx]; CBitcoinSecret vchSecret; bool fGood = vchSecret.SetString(k.get_str()); if (!fGood) @@ -632,15 +636,16 @@ Value signrawtransaction(const Array& params, bool fHelp) #endif // Add previous txouts given in the RPC call: - if (params.size() > 1 && params[1].type() != null_type) { - Array prevTxs = params[1].get_array(); - BOOST_FOREACH(Value& p, prevTxs) { - if (p.type() != obj_type) + if (params.size() > 1 && !params[1].isNull()) { + UniValue prevTxs = params[1].get_array(); + for (unsigned int idx = 0; idx < prevTxs.size(); idx++) { + const UniValue& p = prevTxs[idx]; + if (!p.isObject()) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}"); - Object prevOut = p.get_obj(); + UniValue prevOut = p.get_obj(); - RPCTypeCheck(prevOut, boost::assign::map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)); + RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)); uint256 txid = ParseHashO(prevOut, "txid"); @@ -668,9 +673,9 @@ Value signrawtransaction(const Array& params, bool fHelp) // if redeemScript given and not using the local wallet (private keys // given), add redeemScript to the tempKeystore so it can be signed: if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) { - RPCTypeCheck(prevOut, boost::assign::map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)("redeemScript",str_type)); - Value v = find_value(prevOut, "redeemScript"); - if (!(v == Value::null)) { + RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)("redeemScript",UniValue::VSTR)); + UniValue v = find_value(prevOut, "redeemScript"); + if (!v.isNull()) { vector<unsigned char> rsData(ParseHexV(v, "redeemScript")); CScript redeemScript(rsData.begin(), rsData.end()); tempKeystore.AddCScript(redeemScript); @@ -686,7 +691,7 @@ Value signrawtransaction(const Array& params, bool fHelp) #endif int nHashType = SIGHASH_ALL; - if (params.size() > 3 && params[3].type() != null_type) { + if (params.size() > 3 && !params[3].isNull()) { static map<string, int> mapSigHashValues = boost::assign::map_list_of (string("ALL"), int(SIGHASH_ALL)) @@ -706,7 +711,7 @@ Value signrawtransaction(const Array& params, bool fHelp) bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); // Script verification errors - Array vErrors; + UniValue vErrors(UniValue::VARR); // Sign what we can: for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { @@ -734,7 +739,7 @@ Value signrawtransaction(const Array& params, bool fHelp) } bool fComplete = vErrors.empty(); - Object result; + UniValue result(UniValue::VOBJ); result.push_back(Pair("hex", EncodeHexTx(mergedTx))); result.push_back(Pair("complete", fComplete)); if (!vErrors.empty()) { @@ -744,7 +749,7 @@ Value signrawtransaction(const Array& params, bool fHelp) return result; } -Value sendrawtransaction(const Array& params, bool fHelp) +UniValue sendrawtransaction(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( @@ -768,7 +773,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) ); LOCK(cs_main); - RPCTypeCheck(params, boost::assign::list_of(str_type)(bool_type)); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL)); // parse hex string from parameter CTransaction tx; diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 3f74517a67..3894dd08bb 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -11,6 +11,7 @@ #include "sync.h" #include "ui_interface.h" #include "util.h" +#include "utilmoneystr.h" #include "utilstrencodings.h" #ifdef ENABLE_WALLET #include "wallet/wallet.h" @@ -27,10 +28,10 @@ #include <boost/shared_ptr.hpp> #include <boost/signals2/signal.hpp> #include <boost/thread.hpp> -#include "json/json_spirit_writer_template.h" + +#include "univalue/univalue.h" using namespace boost::asio; -using namespace json_spirit; using namespace RPCServer; using namespace std; @@ -78,71 +79,67 @@ void RPCServer::OnPostCommand(boost::function<void (const CRPCCommand&)> slot) g_rpcSignals.PostCommand.connect(boost::bind(slot, _1)); } -void RPCTypeCheck(const Array& params, - const list<Value_type>& typesExpected, +void RPCTypeCheck(const UniValue& params, + const list<UniValue::VType>& typesExpected, bool fAllowNull) { unsigned int i = 0; - BOOST_FOREACH(Value_type t, typesExpected) + BOOST_FOREACH(UniValue::VType t, typesExpected) { if (params.size() <= i) break; - const Value& v = params[i]; - if (!((v.type() == t) || (fAllowNull && (v.type() == null_type)))) + const UniValue& v = params[i]; + if (!((v.type() == t) || (fAllowNull && (v.isNull())))) { string err = strprintf("Expected type %s, got %s", - Value_type_name[t], Value_type_name[v.type()]); + uvTypeName(t), uvTypeName(v.type())); throw JSONRPCError(RPC_TYPE_ERROR, err); } i++; } } -void RPCTypeCheck(const Object& o, - const map<string, Value_type>& typesExpected, +void RPCTypeCheckObj(const UniValue& o, + const map<string, UniValue::VType>& typesExpected, bool fAllowNull) { - BOOST_FOREACH(const PAIRTYPE(string, Value_type)& t, typesExpected) + BOOST_FOREACH(const PAIRTYPE(string, UniValue::VType)& t, typesExpected) { - const Value& v = find_value(o, t.first); - if (!fAllowNull && v.type() == null_type) + const UniValue& v = find_value(o, t.first); + if (!fAllowNull && v.isNull()) throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first)); - if (!((v.type() == t.second) || (fAllowNull && (v.type() == null_type)))) + if (!((v.type() == t.second) || (fAllowNull && (v.isNull())))) { string err = strprintf("Expected type %s for %s, got %s", - Value_type_name[t.second], t.first, Value_type_name[v.type()]); + uvTypeName(t.second), t.first, uvTypeName(v.type())); throw JSONRPCError(RPC_TYPE_ERROR, err); } } } -static inline int64_t roundint64(double d) -{ - return (int64_t)(d > 0 ? d + 0.5 : d - 0.5); -} - -CAmount AmountFromValue(const Value& value) +CAmount AmountFromValue(const UniValue& value) { - double dAmount = value.get_real(); - if (dAmount <= 0.0 || dAmount > 21000000.0) + if (!value.isReal() && !value.isNum()) + throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number"); + CAmount amount; + if (!ParseMoney(value.getValStr(), amount)) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); - CAmount nAmount = roundint64(dAmount * COIN); - if (!MoneyRange(nAmount)) - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); - return nAmount; + if (!MoneyRange(amount)) + throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range"); + return amount; } -Value ValueFromAmount(const CAmount& amount) +UniValue ValueFromAmount(const CAmount& amount) { - return (double)amount / (double)COIN; + return UniValue(UniValue::VREAL, FormatMoney(amount)); } -uint256 ParseHashV(const Value& v, string strName) +uint256 ParseHashV(const UniValue& v, string strName) { string strHex; - if (v.type() == str_type) + if (v.isStr()) strHex = v.get_str(); if (!IsHex(strHex)) // Note: IsHex("") is false throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); @@ -150,20 +147,20 @@ uint256 ParseHashV(const Value& v, string strName) result.SetHex(strHex); return result; } -uint256 ParseHashO(const Object& o, string strKey) +uint256 ParseHashO(const UniValue& o, string strKey) { return ParseHashV(find_value(o, strKey), strKey); } -vector<unsigned char> ParseHexV(const Value& v, string strName) +vector<unsigned char> ParseHexV(const UniValue& v, string strName) { string strHex; - if (v.type() == str_type) + if (v.isStr()) strHex = v.get_str(); if (!IsHex(strHex)) throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); return ParseHex(strHex); } -vector<unsigned char> ParseHexO(const Object& o, string strKey) +vector<unsigned char> ParseHexO(const UniValue& o, string strKey) { return ParseHexV(find_value(o, strKey), strKey); } @@ -173,7 +170,7 @@ vector<unsigned char> ParseHexO(const Object& o, string strKey) * Note: This interface may still be subject to change. */ -string CRPCTable::help(string strCommand) const +std::string CRPCTable::help(const std::string& strCommand) const { string strRet; string category; @@ -195,7 +192,7 @@ string CRPCTable::help(string strCommand) const continue; try { - Array params; + UniValue params; rpcfn_type pfn = pcmd->actor; if (setDone.insert(pfn).second) (*pfn)(params, true); @@ -228,7 +225,7 @@ string CRPCTable::help(string strCommand) const return strRet; } -Value help(const Array& params, bool fHelp) +UniValue help(const UniValue& params, bool fHelp) { if (fHelp || params.size() > 1) throw runtime_error( @@ -248,7 +245,7 @@ Value help(const Array& params, bool fHelp) } -Value stop(const Array& params, bool fHelp) +UniValue stop(const UniValue& params, bool fHelp) { // Accept the deprecated and ignored 'detach' boolean argument if (fHelp || params.size() > 1) @@ -391,7 +388,7 @@ CRPCTable::CRPCTable() } } -const CRPCCommand *CRPCTable::operator[](string name) const +const CRPCCommand *CRPCTable::operator[](const std::string& name) const { map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name); if (it == mapCommands.end()) @@ -410,14 +407,14 @@ bool HTTPAuthorized(map<string, string>& mapHeaders) return TimingResistantEqual(strUserPass, strRPCUserColonPass); } -void ErrorReply(std::ostream& stream, const Object& objError, const Value& id) +void ErrorReply(std::ostream& stream, const UniValue& objError, const UniValue& id) { // Send error reply from json-rpc error object int nStatus = HTTP_INTERNAL_SERVER_ERROR; int code = find_value(objError, "code").get_int(); if (code == RPC_INVALID_REQUEST) nStatus = HTTP_BAD_REQUEST; else if (code == RPC_METHOD_NOT_FOUND) nStatus = HTTP_NOT_FOUND; - string strReply = JSONRPCReply(Value::null, objError, id); + string strReply = JSONRPCReply(NullUniValue, objError, id); stream << HTTPReply(nStatus, strReply, false) << std::flush; } @@ -824,76 +821,76 @@ void RPCRunLater(const std::string& name, boost::function<void(void)> func, int6 class JSONRequest { public: - Value id; + UniValue id; string strMethod; - Array params; + UniValue params; - JSONRequest() { id = Value::null; } - void parse(const Value& valRequest); + JSONRequest() { id = NullUniValue; } + void parse(const UniValue& valRequest); }; -void JSONRequest::parse(const Value& valRequest) +void JSONRequest::parse(const UniValue& valRequest) { // Parse request - if (valRequest.type() != obj_type) + if (!valRequest.isObject()) throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); - const Object& request = valRequest.get_obj(); + const UniValue& request = valRequest.get_obj(); // Parse id now so errors from here on will have the id id = find_value(request, "id"); // Parse method - Value valMethod = find_value(request, "method"); - if (valMethod.type() == null_type) + UniValue valMethod = find_value(request, "method"); + if (valMethod.isNull()) throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method"); - if (valMethod.type() != str_type) + if (!valMethod.isStr()) throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); strMethod = valMethod.get_str(); if (strMethod != "getblocktemplate") LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod)); // Parse params - Value valParams = find_value(request, "params"); - if (valParams.type() == array_type) + UniValue valParams = find_value(request, "params"); + if (valParams.isArray()) params = valParams.get_array(); - else if (valParams.type() == null_type) - params = Array(); + else if (valParams.isNull()) + params = UniValue(UniValue::VARR); else throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array"); } -static Object JSONRPCExecOne(const Value& req) +static UniValue JSONRPCExecOne(const UniValue& req) { - Object rpc_result; + UniValue rpc_result(UniValue::VOBJ); JSONRequest jreq; try { jreq.parse(req); - Value result = tableRPC.execute(jreq.strMethod, jreq.params); - rpc_result = JSONRPCReplyObj(result, Value::null, jreq.id); + UniValue result = tableRPC.execute(jreq.strMethod, jreq.params); + rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id); } - catch (const Object& objError) + catch (const UniValue& objError) { - rpc_result = JSONRPCReplyObj(Value::null, objError, jreq.id); + rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id); } catch (const std::exception& e) { - rpc_result = JSONRPCReplyObj(Value::null, + rpc_result = JSONRPCReplyObj(NullUniValue, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); } return rpc_result; } -static string JSONRPCExecBatch(const Array& vReq) +static string JSONRPCExecBatch(const UniValue& vReq) { - Array ret; + UniValue ret(UniValue::VARR); for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++) ret.push_back(JSONRPCExecOne(vReq[reqIdx])); - return write_string(Value(ret), false) + "\n"; + return ret.write() + "\n"; } static bool HTTPReq_JSONRPC(AcceptedConnection *conn, @@ -924,8 +921,8 @@ static bool HTTPReq_JSONRPC(AcceptedConnection *conn, try { // Parse request - Value valRequest; - if (!read_string(strRequest, valRequest)) + UniValue valRequest; + if (!valRequest.read(strRequest)) throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); // Return immediately if in warmup @@ -938,23 +935,23 @@ static bool HTTPReq_JSONRPC(AcceptedConnection *conn, string strReply; // singleton request - if (valRequest.type() == obj_type) { + if (valRequest.isObject()) { jreq.parse(valRequest); - Value result = tableRPC.execute(jreq.strMethod, jreq.params); + UniValue result = tableRPC.execute(jreq.strMethod, jreq.params); // Send reply - strReply = JSONRPCReply(result, Value::null, jreq.id); + strReply = JSONRPCReply(result, NullUniValue, jreq.id); // array of requests - } else if (valRequest.type() == array_type) + } else if (valRequest.isArray()) strReply = JSONRPCExecBatch(valRequest.get_array()); else throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, strReply.size()) << strReply << std::flush; } - catch (const Object& objError) + catch (const UniValue& objError) { ErrorReply(conn->stream(), objError, jreq.id); return false; @@ -1004,7 +1001,7 @@ void ServiceConnection(AcceptedConnection *conn) } } -json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array ¶ms) const +UniValue CRPCTable::execute(const std::string &strMethod, const UniValue ¶ms) const { // Find method const CRPCCommand *pcmd = tableRPC[strMethod]; @@ -1026,11 +1023,13 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s g_rpcSignals.PostCommand(*pcmd); } -std::string HelpExampleCli(string methodname, string args){ +std::string HelpExampleCli(const std::string& methodname, const std::string& args) +{ return "> bitcoin-cli " + methodname + " " + args + "\n"; } -std::string HelpExampleRpc(string methodname, string args){ +std::string HelpExampleRpc(const std::string& methodname, const std::string& args) +{ return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", " "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; } diff --git a/src/rpcserver.h b/src/rpcserver.h index 30a5b28db7..7b462a8b79 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -15,9 +15,9 @@ #include <stdint.h> #include <string> -#include "json/json_spirit_reader_template.h" -#include "json/json_spirit_utils.h" -#include "json/json_spirit_writer_template.h" +#include <boost/function.hpp> + +#include "univalue/univalue.h" class CRPCCommand; @@ -55,7 +55,7 @@ void StopRPCThreads(); /** Query whether RPC is running */ bool IsRPCRunning(); -/** +/** * Set the RPC warmup status. When this is done, all RPC calls will error out * immediately with RPC_IN_WARMUP. */ @@ -71,14 +71,15 @@ bool RPCIsInWarmup(std::string *statusOut); * the right number of arguments are passed, just that any passed are the correct type. * Use like: RPCTypeCheck(params, boost::assign::list_of(str_type)(int_type)(obj_type)); */ -void RPCTypeCheck(const json_spirit::Array& params, - const std::list<json_spirit::Value_type>& typesExpected, bool fAllowNull=false); -/** - * Check for expected keys/value types in an Object. - * Use like: RPCTypeCheck(object, boost::assign::map_list_of("name", str_type)("value", int_type)); - */ -void RPCTypeCheck(const json_spirit::Object& o, - const std::map<std::string, json_spirit::Value_type>& typesExpected, bool fAllowNull=false); +void RPCTypeCheck(const UniValue& params, + const std::list<UniValue::VType>& typesExpected, bool fAllowNull=false); + +/* + Check for expected keys/value types in an Object. + Use like: RPCTypeCheckObj(object, boost::assign::map_list_of("name", str_type)("value", int_type)); +*/ +void RPCTypeCheckObj(const UniValue& o, + const std::map<std::string, UniValue::VType>& typesExpected, bool fAllowNull=false); /** * Run func nSeconds from now. Uses boost deadline timers. @@ -89,7 +90,7 @@ void RPCRunLater(const std::string& name, boost::function<void(void)> func, int6 //! Convert boost::asio address to CNetAddr extern CNetAddr BoostAsioToCNetAddr(boost::asio::ip::address address); -typedef json_spirit::Value(*rpcfn_type)(const json_spirit::Array& params, bool fHelp); +typedef UniValue(*rpcfn_type)(const UniValue& params, bool fHelp); class CRPCCommand { @@ -109,17 +110,17 @@ private: std::map<std::string, const CRPCCommand*> mapCommands; public: CRPCTable(); - const CRPCCommand* operator[](std::string name) const; - std::string help(std::string name) const; + const CRPCCommand* operator[](const std::string& name) const; + std::string help(const std::string& name) const; /** * Execute a method. * @param method Method to execute - * @param params Array of arguments (JSON objects) + * @param params UniValue Array of arguments (JSON objects) * @returns Result of the call. - * @throws an exception (json_spirit::Value) when an error happens. + * @throws an exception (UniValue) when an error happens. */ - json_spirit::Value execute(const std::string &method, const json_spirit::Array ¶ms) const; + UniValue execute(const std::string &method, const UniValue ¶ms) const; }; extern const CRPCTable tableRPC; @@ -128,113 +129,113 @@ extern const CRPCTable tableRPC; * Utilities: convert hex-encoded Values * (throws error if not hex). */ -extern uint256 ParseHashV(const json_spirit::Value& v, std::string strName); -extern uint256 ParseHashO(const json_spirit::Object& o, std::string strKey); -extern std::vector<unsigned char> ParseHexV(const json_spirit::Value& v, std::string strName); -extern std::vector<unsigned char> ParseHexO(const json_spirit::Object& o, std::string strKey); +extern uint256 ParseHashV(const UniValue& v, std::string strName); +extern uint256 ParseHashO(const UniValue& o, std::string strKey); +extern std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName); +extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey); extern void InitRPCMining(); extern void ShutdownRPCMining(); extern int64_t nWalletUnlockTime; -extern CAmount AmountFromValue(const json_spirit::Value& value); -extern json_spirit::Value ValueFromAmount(const CAmount& amount); +extern CAmount AmountFromValue(const UniValue& value); +extern UniValue ValueFromAmount(const CAmount& amount); extern double GetDifficulty(const CBlockIndex* blockindex = NULL); extern std::string HelpRequiringPassphrase(); -extern std::string HelpExampleCli(std::string methodname, std::string args); -extern std::string HelpExampleRpc(std::string methodname, std::string args); +extern std::string HelpExampleCli(const std::string& methodname, const std::string& args); +extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args); extern void EnsureWalletIsUnlocked(); -extern json_spirit::Value getconnectioncount(const json_spirit::Array& params, bool fHelp); // in rpcnet.cpp -extern json_spirit::Value getpeerinfo(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value ping(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value addnode(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getaddednodeinfo(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getnettotals(const json_spirit::Array& params, bool fHelp); - -extern json_spirit::Value dumpprivkey(const json_spirit::Array& params, bool fHelp); // in rpcdump.cpp -extern json_spirit::Value importprivkey(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value importaddress(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value dumpwallet(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fHelp); - -extern json_spirit::Value getgenerate(const json_spirit::Array& params, bool fHelp); // in rpcmining.cpp -extern json_spirit::Value setgenerate(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value generate(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getnetworkhashps(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getmininginfo(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value prioritisetransaction(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getblocktemplate(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value submitblock(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value estimatefee(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value estimatepriority(const json_spirit::Array& params, bool fHelp); - -extern json_spirit::Value getnewaddress(const json_spirit::Array& params, bool fHelp); // in rpcwallet.cpp -extern json_spirit::Value getaccountaddress(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getrawchangeaddress(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value setaccount(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getaccount(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getaddressesbyaccount(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value sendtoaddress(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value signmessage(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value verifymessage(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getreceivedbyaddress(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getreceivedbyaccount(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getbalance(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getunconfirmedbalance(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value movecmd(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value sendfrom(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value sendmany(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value addmultisigaddress(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value createmultisig(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value listreceivedbyaddress(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value listreceivedbyaccount(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value listtransactions(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value listaddressgroupings(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value listaccounts(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value listsinceblock(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value gettransaction(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value backupwallet(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value keypoolrefill(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value walletpassphrase(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value walletpassphrasechange(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value walletlock(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value encryptwallet(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value validateaddress(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getinfo(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getwalletinfo(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getblockchaininfo(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getnetworkinfo(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value setmocktime(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value resendwallettransactions(const json_spirit::Array& params, bool fHelp); - -extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp -extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value lockunspent(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value listlockunspent(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value createrawtransaction(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value decoderawtransaction(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value decodescript(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value signrawtransaction(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value sendrawtransaction(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value gettxoutproof(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value verifytxoutproof(const json_spirit::Array& params, bool fHelp); - -extern json_spirit::Value getblockcount(const json_spirit::Array& params, bool fHelp); // in rpcblockchain.cpp -extern json_spirit::Value getbestblockhash(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getdifficulty(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value settxfee(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getmempoolinfo(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getrawmempool(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getblockhash(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getblock(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value gettxoutsetinfo(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value gettxout(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value verifychain(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getchaintips(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value invalidateblock(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value reconsiderblock(const json_spirit::Array& params, bool fHelp); +extern UniValue getconnectioncount(const UniValue& params, bool fHelp); // in rpcnet.cpp +extern UniValue getpeerinfo(const UniValue& params, bool fHelp); +extern UniValue ping(const UniValue& params, bool fHelp); +extern UniValue addnode(const UniValue& params, bool fHelp); +extern UniValue getaddednodeinfo(const UniValue& params, bool fHelp); +extern UniValue getnettotals(const UniValue& params, bool fHelp); + +extern UniValue dumpprivkey(const UniValue& params, bool fHelp); // in rpcdump.cpp +extern UniValue importprivkey(const UniValue& params, bool fHelp); +extern UniValue importaddress(const UniValue& params, bool fHelp); +extern UniValue dumpwallet(const UniValue& params, bool fHelp); +extern UniValue importwallet(const UniValue& params, bool fHelp); + +extern UniValue getgenerate(const UniValue& params, bool fHelp); // in rpcmining.cpp +extern UniValue setgenerate(const UniValue& params, bool fHelp); +extern UniValue generate(const UniValue& params, bool fHelp); +extern UniValue getnetworkhashps(const UniValue& params, bool fHelp); +extern UniValue getmininginfo(const UniValue& params, bool fHelp); +extern UniValue prioritisetransaction(const UniValue& params, bool fHelp); +extern UniValue getblocktemplate(const UniValue& params, bool fHelp); +extern UniValue submitblock(const UniValue& params, bool fHelp); +extern UniValue estimatefee(const UniValue& params, bool fHelp); +extern UniValue estimatepriority(const UniValue& params, bool fHelp); + +extern UniValue getnewaddress(const UniValue& params, bool fHelp); // in rpcwallet.cpp +extern UniValue getaccountaddress(const UniValue& params, bool fHelp); +extern UniValue getrawchangeaddress(const UniValue& params, bool fHelp); +extern UniValue setaccount(const UniValue& params, bool fHelp); +extern UniValue getaccount(const UniValue& params, bool fHelp); +extern UniValue getaddressesbyaccount(const UniValue& params, bool fHelp); +extern UniValue sendtoaddress(const UniValue& params, bool fHelp); +extern UniValue signmessage(const UniValue& params, bool fHelp); +extern UniValue verifymessage(const UniValue& params, bool fHelp); +extern UniValue getreceivedbyaddress(const UniValue& params, bool fHelp); +extern UniValue getreceivedbyaccount(const UniValue& params, bool fHelp); +extern UniValue getbalance(const UniValue& params, bool fHelp); +extern UniValue getunconfirmedbalance(const UniValue& params, bool fHelp); +extern UniValue movecmd(const UniValue& params, bool fHelp); +extern UniValue sendfrom(const UniValue& params, bool fHelp); +extern UniValue sendmany(const UniValue& params, bool fHelp); +extern UniValue addmultisigaddress(const UniValue& params, bool fHelp); +extern UniValue createmultisig(const UniValue& params, bool fHelp); +extern UniValue listreceivedbyaddress(const UniValue& params, bool fHelp); +extern UniValue listreceivedbyaccount(const UniValue& params, bool fHelp); +extern UniValue listtransactions(const UniValue& params, bool fHelp); +extern UniValue listaddressgroupings(const UniValue& params, bool fHelp); +extern UniValue listaccounts(const UniValue& params, bool fHelp); +extern UniValue listsinceblock(const UniValue& params, bool fHelp); +extern UniValue gettransaction(const UniValue& params, bool fHelp); +extern UniValue backupwallet(const UniValue& params, bool fHelp); +extern UniValue keypoolrefill(const UniValue& params, bool fHelp); +extern UniValue walletpassphrase(const UniValue& params, bool fHelp); +extern UniValue walletpassphrasechange(const UniValue& params, bool fHelp); +extern UniValue walletlock(const UniValue& params, bool fHelp); +extern UniValue encryptwallet(const UniValue& params, bool fHelp); +extern UniValue validateaddress(const UniValue& params, bool fHelp); +extern UniValue getinfo(const UniValue& params, bool fHelp); +extern UniValue getwalletinfo(const UniValue& params, bool fHelp); +extern UniValue getblockchaininfo(const UniValue& params, bool fHelp); +extern UniValue getnetworkinfo(const UniValue& params, bool fHelp); +extern UniValue setmocktime(const UniValue& params, bool fHelp); +extern UniValue resendwallettransactions(const UniValue& params, bool fHelp); + +extern UniValue getrawtransaction(const UniValue& params, bool fHelp); // in rcprawtransaction.cpp +extern UniValue listunspent(const UniValue& params, bool fHelp); +extern UniValue lockunspent(const UniValue& params, bool fHelp); +extern UniValue listlockunspent(const UniValue& params, bool fHelp); +extern UniValue createrawtransaction(const UniValue& params, bool fHelp); +extern UniValue decoderawtransaction(const UniValue& params, bool fHelp); +extern UniValue decodescript(const UniValue& params, bool fHelp); +extern UniValue signrawtransaction(const UniValue& params, bool fHelp); +extern UniValue sendrawtransaction(const UniValue& params, bool fHelp); +extern UniValue gettxoutproof(const UniValue& params, bool fHelp); +extern UniValue verifytxoutproof(const UniValue& params, bool fHelp); + +extern UniValue getblockcount(const UniValue& params, bool fHelp); // in rpcblockchain.cpp +extern UniValue getbestblockhash(const UniValue& params, bool fHelp); +extern UniValue getdifficulty(const UniValue& params, bool fHelp); +extern UniValue settxfee(const UniValue& params, bool fHelp); +extern UniValue getmempoolinfo(const UniValue& params, bool fHelp); +extern UniValue getrawmempool(const UniValue& params, bool fHelp); +extern UniValue getblockhash(const UniValue& params, bool fHelp); +extern UniValue getblock(const UniValue& params, bool fHelp); +extern UniValue gettxoutsetinfo(const UniValue& params, bool fHelp); +extern UniValue gettxout(const UniValue& params, bool fHelp); +extern UniValue verifychain(const UniValue& params, bool fHelp); +extern UniValue getchaintips(const UniValue& params, bool fHelp); +extern UniValue invalidateblock(const UniValue& params, bool fHelp); +extern UniValue reconsiderblock(const UniValue& params, bool fHelp); // in rest.cpp extern bool HTTPReq_REST(AcceptedConnection *conn, diff --git a/src/scheduler.cpp b/src/scheduler.cpp new file mode 100644 index 0000000000..c42eb7244d --- /dev/null +++ b/src/scheduler.cpp @@ -0,0 +1,126 @@ +// Copyright (c) 2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "scheduler.h" + +#include <assert.h> +#include <boost/bind.hpp> +#include <utility> + +CScheduler::CScheduler() : nThreadsServicingQueue(0), stopRequested(false), stopWhenEmpty(false) +{ +} + +CScheduler::~CScheduler() +{ + assert(nThreadsServicingQueue == 0); +} + + +#if BOOST_VERSION < 105000 +static boost::system_time toPosixTime(const boost::chrono::system_clock::time_point& t) +{ + return boost::posix_time::from_time_t(boost::chrono::system_clock::to_time_t(t)); +} +#endif + +void CScheduler::serviceQueue() +{ + boost::unique_lock<boost::mutex> lock(newTaskMutex); + ++nThreadsServicingQueue; + + // newTaskMutex is locked throughout this loop EXCEPT + // when the thread is waiting or when the user's function + // is called. + while (!shouldStop()) { + try { + while (!shouldStop() && taskQueue.empty()) { + // Wait until there is something to do. + newTaskScheduled.wait(lock); + } + + // Wait until either there is a new task, or until + // the time of the first item on the queue: + +// wait_until needs boost 1.50 or later; older versions have timed_wait: +#if BOOST_VERSION < 105000 + while (!shouldStop() && !taskQueue.empty() && + newTaskScheduled.timed_wait(lock, toPosixTime(taskQueue.begin()->first))) { + // Keep waiting until timeout + } +#else + while (!shouldStop() && !taskQueue.empty() && + newTaskScheduled.wait_until(lock, taskQueue.begin()->first) != boost::cv_status::timeout) { + // Keep waiting until timeout + } +#endif + // If there are multiple threads, the queue can empty while we're waiting (another + // thread may service the task we were waiting on). + if (shouldStop() || taskQueue.empty()) + continue; + + Function f = taskQueue.begin()->second; + taskQueue.erase(taskQueue.begin()); + + // Unlock before calling f, so it can reschedule itself or another task + // without deadlocking: + lock.unlock(); + f(); + lock.lock(); + } catch (...) { + --nThreadsServicingQueue; + throw; + } + } + --nThreadsServicingQueue; +} + +void CScheduler::stop(bool drain) +{ + { + boost::unique_lock<boost::mutex> lock(newTaskMutex); + if (drain) + stopWhenEmpty = true; + else + stopRequested = true; + } + newTaskScheduled.notify_all(); +} + +void CScheduler::schedule(CScheduler::Function f, boost::chrono::system_clock::time_point t) +{ + { + boost::unique_lock<boost::mutex> lock(newTaskMutex); + taskQueue.insert(std::make_pair(t, f)); + } + newTaskScheduled.notify_one(); +} + +void CScheduler::scheduleFromNow(CScheduler::Function f, int64_t deltaSeconds) +{ + schedule(f, boost::chrono::system_clock::now() + boost::chrono::seconds(deltaSeconds)); +} + +static void Repeat(CScheduler* s, CScheduler::Function f, int64_t deltaSeconds) +{ + f(); + s->scheduleFromNow(boost::bind(&Repeat, s, f, deltaSeconds), deltaSeconds); +} + +void CScheduler::scheduleEvery(CScheduler::Function f, int64_t deltaSeconds) +{ + scheduleFromNow(boost::bind(&Repeat, this, f, deltaSeconds), deltaSeconds); +} + +size_t CScheduler::getQueueInfo(boost::chrono::system_clock::time_point &first, + boost::chrono::system_clock::time_point &last) const +{ + boost::unique_lock<boost::mutex> lock(newTaskMutex); + size_t result = taskQueue.size(); + if (!taskQueue.empty()) { + first = taskQueue.begin()->first; + last = taskQueue.rbegin()->first; + } + return result; +} diff --git a/src/scheduler.h b/src/scheduler.h new file mode 100644 index 0000000000..436659e58b --- /dev/null +++ b/src/scheduler.h @@ -0,0 +1,83 @@ +// Copyright (c) 2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_SCHEDULER_H +#define BITCOIN_SCHEDULER_H + +// +// NOTE: +// boost::thread / boost::function / boost::chrono should be ported to +// std::thread / std::function / std::chrono when we support C++11. +// +#include <boost/function.hpp> +#include <boost/chrono/chrono.hpp> +#include <boost/thread.hpp> +#include <map> + +// +// Simple class for background tasks that should be run +// periodically or once "after a while" +// +// Usage: +// +// CScheduler* s = new CScheduler(); +// s->scheduleFromNow(doSomething, 11); // Assuming a: void doSomething() { } +// s->scheduleFromNow(boost::bind(Class::func, this, argument), 3); +// boost::thread* t = new boost::thread(boost::bind(CScheduler::serviceQueue, s)); +// +// ... then at program shutdown, clean up the thread running serviceQueue: +// t->interrupt(); +// t->join(); +// delete t; +// delete s; // Must be done after thread is interrupted/joined. +// + +class CScheduler +{ +public: + CScheduler(); + ~CScheduler(); + + typedef boost::function<void(void)> Function; + + // Call func at/after time t + void schedule(Function f, boost::chrono::system_clock::time_point t); + + // Convenience method: call f once deltaSeconds from now + void scheduleFromNow(Function f, int64_t deltaSeconds); + + // Another convenience method: call f approximately + // every deltaSeconds forever, starting deltaSeconds from now. + // To be more precise: every time f is finished, it + // is rescheduled to run deltaSeconds later. If you + // need more accurate scheduling, don't use this method. + void scheduleEvery(Function f, int64_t deltaSeconds); + + // To keep things as simple as possible, there is no unschedule. + + // Services the queue 'forever'. Should be run in a thread, + // and interrupted using boost::interrupt_thread + void serviceQueue(); + + // Tell any threads running serviceQueue to stop as soon as they're + // done servicing whatever task they're currently servicing (drain=false) + // or when there is no work left to be done (drain=true) + void stop(bool drain=false); + + // Returns number of tasks waiting to be serviced, + // and first and last task times + size_t getQueueInfo(boost::chrono::system_clock::time_point &first, + boost::chrono::system_clock::time_point &last) const; + +private: + std::multimap<boost::chrono::system_clock::time_point, Function> taskQueue; + boost::condition_variable newTaskScheduled; + mutable boost::mutex newTaskMutex; + int nThreadsServicingQueue; + bool stopRequested; + bool stopWhenEmpty; + bool shouldStop() { return stopRequested || (stopWhenEmpty && taskQueue.empty()); } +}; + +#endif diff --git a/src/script/script.h b/src/script/script.h index ed456f5c5a..d5045005be 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -6,6 +6,8 @@ #ifndef BITCOIN_SCRIPT_SCRIPT_H #define BITCOIN_SCRIPT_SCRIPT_H +#include "crypto/common.h" + #include <assert.h> #include <climits> #include <limits> @@ -14,7 +16,6 @@ #include <string.h> #include <string> #include <vector> -#include "crypto/common.h" static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes diff --git a/src/serialize.h b/src/serialize.h index 741f78f8b4..53d8af142f 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -6,6 +6,8 @@ #ifndef BITCOIN_SERIALIZE_H #define BITCOIN_SERIALIZE_H +#include "compat/endian.h" + #include <algorithm> #include <assert.h> #include <ios> @@ -18,8 +20,6 @@ #include <utility> #include <vector> -#include "compat/endian.h" - class CScript; static const unsigned int MAX_SIZE = 0x02000000; diff --git a/src/support/pagelocker.h b/src/support/pagelocker.h index 3fd793072f..88b95cce73 100644 --- a/src/support/pagelocker.h +++ b/src/support/pagelocker.h @@ -37,7 +37,6 @@ public: ~LockedPageManagerBase() { - assert(this->GetLockedPageCount() == 0); } diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp index 6b6df5199a..22cb475e02 100644 --- a/src/test/alert_tests.cpp +++ b/src/test/alert_tests.cpp @@ -7,10 +7,12 @@ // #include "alert.h" +#include "chain.h" +#include "chainparams.h" #include "clientversion.h" #include "data/alertTests.raw.h" -#include "chainparams.h" +#include "main.h" #include "serialize.h" #include "streams.h" #include "util.h" @@ -193,4 +195,65 @@ BOOST_AUTO_TEST_CASE(AlertNotify) SetMockTime(0); } +static bool falseFunc() { return false; } + +BOOST_AUTO_TEST_CASE(PartitionAlert) +{ + // Test PartitionCheck + CCriticalSection csDummy; + CChain chainDummy; + CBlockIndex indexDummy[100]; + CChainParams& params = Params(CBaseChainParams::MAIN); + int64_t nPowTargetSpacing = params.GetConsensus().nPowTargetSpacing; + + // Generate fake blockchain timestamps relative to + // an arbitrary time: + int64_t now = 1427379054; + SetMockTime(now); + for (int i = 0; i < 100; i++) + { + indexDummy[i].phashBlock = NULL; + if (i == 0) indexDummy[i].pprev = NULL; + else indexDummy[i].pprev = &indexDummy[i-1]; + indexDummy[i].nHeight = i; + indexDummy[i].nTime = now - (100-i)*nPowTargetSpacing; + // Other members don't matter, the partition check code doesn't + // use them + } + chainDummy.SetTip(&indexDummy[99]); + + // Test 1: chain with blocks every nPowTargetSpacing seconds, + // as normal, no worries: + PartitionCheck(falseFunc, csDummy, chainDummy, nPowTargetSpacing); + BOOST_CHECK(strMiscWarning.empty()); + + // Test 2: go 3.5 hours without a block, expect a warning: + now += 3*60*60+30*60; + SetMockTime(now); + PartitionCheck(falseFunc, csDummy, chainDummy, nPowTargetSpacing); + BOOST_CHECK(!strMiscWarning.empty()); + BOOST_TEST_MESSAGE(std::string("Got alert text: ")+strMiscWarning); + strMiscWarning = ""; + + // Test 3: test the "partition alerts only go off once per day" + // code: + now += 60*10; + SetMockTime(now); + PartitionCheck(falseFunc, csDummy, chainDummy, nPowTargetSpacing); + BOOST_CHECK(strMiscWarning.empty()); + + // Test 4: get 2.5 times as many blocks as expected: + now += 60*60*24; // Pretend it is a day later + SetMockTime(now); + int64_t quickSpacing = nPowTargetSpacing*2/5; + for (int i = 0; i < 100; i++) // Tweak chain timestamps: + indexDummy[i].nTime = now - (100-i)*quickSpacing; + PartitionCheck(falseFunc, csDummy, chainDummy, nPowTargetSpacing); + BOOST_CHECK(!strMiscWarning.empty()); + BOOST_TEST_MESSAGE(std::string("Got alert text: ")+strMiscWarning); + strMiscWarning = ""; + + SetMockTime(0); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index f07dd7a7db..9e74f5f427 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -17,23 +17,20 @@ #include <boost/foreach.hpp> #include <boost/test/unit_test.hpp> -#include "json/json_spirit_reader_template.h" -#include "json/json_spirit_utils.h" -#include "json/json_spirit_writer_template.h" -using namespace json_spirit; -extern Array read_json(const std::string& jsondata); +#include "univalue/univalue.h" + +extern UniValue read_json(const std::string& jsondata); BOOST_FIXTURE_TEST_SUITE(base58_tests, BasicTestingSetup) // Goal: test low-level base58 encoding functionality BOOST_AUTO_TEST_CASE(base58_EncodeBase58) { - Array tests = read_json(std::string(json_tests::base58_encode_decode, json_tests::base58_encode_decode + sizeof(json_tests::base58_encode_decode))); - BOOST_FOREACH(Value& tv, tests) - { - Array test = tv.get_array(); - std::string strTest = write_string(tv, false); + UniValue tests = read_json(std::string(json_tests::base58_encode_decode, json_tests::base58_encode_decode + sizeof(json_tests::base58_encode_decode))); + for (unsigned int idx = 0; idx < tests.size(); idx++) { + UniValue test = tests[idx]; + std::string strTest = test.write(); if (test.size() < 2) // Allow for extra stuff (useful for comments) { BOOST_ERROR("Bad test: " << strTest); @@ -50,13 +47,12 @@ BOOST_AUTO_TEST_CASE(base58_EncodeBase58) // Goal: test low-level base58 decoding functionality BOOST_AUTO_TEST_CASE(base58_DecodeBase58) { - Array tests = read_json(std::string(json_tests::base58_encode_decode, json_tests::base58_encode_decode + sizeof(json_tests::base58_encode_decode))); + UniValue tests = read_json(std::string(json_tests::base58_encode_decode, json_tests::base58_encode_decode + sizeof(json_tests::base58_encode_decode))); std::vector<unsigned char> result; - BOOST_FOREACH(Value& tv, tests) - { - Array test = tv.get_array(); - std::string strTest = write_string(tv, false); + for (unsigned int idx = 0; idx < tests.size(); idx++) { + UniValue test = tests[idx]; + std::string strTest = test.write(); if (test.size() < 2) // Allow for extra stuff (useful for comments) { BOOST_ERROR("Bad test: " << strTest); @@ -124,16 +120,15 @@ public: // Goal: check that parsed keys match test payload BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) { - Array tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid))); + UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid))); std::vector<unsigned char> result; CBitcoinSecret secret; CBitcoinAddress addr; SelectParams(CBaseChainParams::MAIN); - BOOST_FOREACH(Value& tv, tests) - { - Array test = tv.get_array(); - std::string strTest = write_string(tv, false); + for (unsigned int idx = 0; idx < tests.size(); idx++) { + UniValue test = tests[idx]; + std::string strTest = test.write(); if (test.size() < 3) // Allow for extra stuff (useful for comments) { BOOST_ERROR("Bad test: " << strTest); @@ -141,7 +136,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) } std::string exp_base58string = test[0].get_str(); std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str()); - const Object &metadata = test[2].get_obj(); + const UniValue &metadata = test[2].get_obj(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); bool isTestnet = find_value(metadata, "isTestnet").get_bool(); if (isTestnet) @@ -183,12 +178,12 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) // Goal: check that generated keys match test vectors BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) { - Array tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid))); + UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid))); std::vector<unsigned char> result; - BOOST_FOREACH(Value& tv, tests) - { - Array test = tv.get_array(); - std::string strTest = write_string(tv, false); + + for (unsigned int idx = 0; idx < tests.size(); idx++) { + UniValue test = tests[idx]; + std::string strTest = test.write(); if (test.size() < 3) // Allow for extra stuff (useful for comments) { BOOST_ERROR("Bad test: " << strTest); @@ -196,7 +191,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) } std::string exp_base58string = test[0].get_str(); std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str()); - const Object &metadata = test[2].get_obj(); + const UniValue &metadata = test[2].get_obj(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); bool isTestnet = find_value(metadata, "isTestnet").get_bool(); if (isTestnet) @@ -251,15 +246,14 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) // Goal: check that base58 parsing code is robust against a variety of corrupted data BOOST_AUTO_TEST_CASE(base58_keys_invalid) { - Array tests = read_json(std::string(json_tests::base58_keys_invalid, json_tests::base58_keys_invalid + sizeof(json_tests::base58_keys_invalid))); // Negative testcases + UniValue tests = read_json(std::string(json_tests::base58_keys_invalid, json_tests::base58_keys_invalid + sizeof(json_tests::base58_keys_invalid))); // Negative testcases std::vector<unsigned char> result; CBitcoinSecret secret; CBitcoinAddress addr; - BOOST_FOREACH(Value& tv, tests) - { - Array test = tv.get_array(); - std::string strTest = write_string(tv, false); + for (unsigned int idx = 0; idx < tests.size(); idx++) { + UniValue test = tests[idx]; + std::string strTest = test.write(); if (test.size() < 1) // Allow for extra stuff (useful for comments) { BOOST_ERROR("Bad test: " << strTest); diff --git a/src/test/checkblock_tests.cpp b/src/test/checkblock_tests.cpp index 7abfad151e..51530c4de5 100644 --- a/src/test/checkblock_tests.cpp +++ b/src/test/checkblock_tests.cpp @@ -2,16 +2,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -// -// Unit tests for block.CheckBlock() -// - - - #include "clientversion.h" +#include "consensus/validation.h" #include "main.h" -#include "utiltime.h" #include "test/test_bitcoin.h" +#include "utiltime.h" #include <cstdio> diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 2e2cc2214b..34b311b804 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -59,6 +59,24 @@ public: bool GetStats(CCoinsStats& stats) const { return false; } }; + +class CCoinsViewCacheTest : public CCoinsViewCache +{ +public: + CCoinsViewCacheTest(CCoinsView* base) : CCoinsViewCache(base) {} + + void SelfTest() const + { + // Manually recompute the dynamic usage of the whole data, and compare it. + size_t ret = memusage::DynamicUsage(cacheCoins); + for (CCoinsMap::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) { + ret += memusage::DynamicUsage(it->second.coins); + } + BOOST_CHECK_EQUAL(memusage::DynamicUsage(*this), ret); + } + +}; + } BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup) @@ -90,8 +108,8 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) // The cache stack. CCoinsViewTest base; // A CCoinsViewTest at the bottom. - std::vector<CCoinsViewCache*> stack; // A stack of CCoinsViewCaches on top. - stack.push_back(new CCoinsViewCache(&base)); // Start with one cache. + std::vector<CCoinsViewCacheTest*> stack; // A stack of CCoinsViewCaches on top. + stack.push_back(new CCoinsViewCacheTest(&base)); // Start with one cache. // Use a limited set of random transaction ids, so we do test overwriting entries. std::vector<uint256> txids; @@ -136,6 +154,9 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) missed_an_entry = true; } } + BOOST_FOREACH(const CCoinsViewCacheTest *test, stack) { + test->SelfTest(); + } } if (insecure_rand() % 100 == 0) { @@ -152,7 +173,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) } else { removed_all_caches = true; } - stack.push_back(new CCoinsViewCache(tip)); + stack.push_back(new CCoinsViewCacheTest(tip)); if (stack.size() == 4) { reached_4_caches = true; } diff --git a/src/test/main_tests.cpp b/src/test/main_tests.cpp index 9ec533bcca..21ae46d6e9 100644 --- a/src/test/main_tests.cpp +++ b/src/test/main_tests.cpp @@ -2,25 +2,58 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "primitives/transaction.h" +#include "chainparams.h" #include "main.h" #include "test/test_bitcoin.h" +#include <boost/signals2/signal.hpp> #include <boost/test/unit_test.hpp> BOOST_FIXTURE_TEST_SUITE(main_tests, TestingSetup) +static void TestBlockSubsidyHalvings(const Consensus::Params& consensusParams) +{ + int maxHalvings = 64; + CAmount nInitialSubsidy = 50 * COIN; + + CAmount nPreviousSubsidy = nInitialSubsidy * 2; // for height == 0 + BOOST_CHECK_EQUAL(nPreviousSubsidy, nInitialSubsidy * 2); + for (int nHalvings = 0; nHalvings < maxHalvings; nHalvings++) { + int nHeight = nHalvings * consensusParams.nSubsidyHalvingInterval; + CAmount nSubsidy = GetBlockSubsidy(nHeight, consensusParams); + BOOST_CHECK(nSubsidy <= nInitialSubsidy); + BOOST_CHECK_EQUAL(nSubsidy, nPreviousSubsidy / 2); + nPreviousSubsidy = nSubsidy; + } + BOOST_CHECK_EQUAL(GetBlockSubsidy(maxHalvings * consensusParams.nSubsidyHalvingInterval, consensusParams), 0); +} + +static void TestBlockSubsidyHalvings(int nSubsidyHalvingInterval) +{ + Consensus::Params consensusParams; + consensusParams.nSubsidyHalvingInterval = nSubsidyHalvingInterval; + TestBlockSubsidyHalvings(consensusParams); +} + +BOOST_AUTO_TEST_CASE(block_subsidy_test) +{ + TestBlockSubsidyHalvings(Params(CBaseChainParams::MAIN).GetConsensus()); // As in main + TestBlockSubsidyHalvings(150); // As in regtest + TestBlockSubsidyHalvings(1000); // Just another interval +} + BOOST_AUTO_TEST_CASE(subsidy_limit_test) { + const Consensus::Params& consensusParams = Params(CBaseChainParams::MAIN).GetConsensus(); CAmount nSum = 0; for (int nHeight = 0; nHeight < 14000000; nHeight += 1000) { - CAmount nSubsidy = GetBlockValue(nHeight, 0); + CAmount nSubsidy = GetBlockSubsidy(nHeight, consensusParams); BOOST_CHECK(nSubsidy <= 50 * COIN); nSum += nSubsidy * 1000; BOOST_CHECK(MoneyRange(nSum)); } - BOOST_CHECK(nSum == 2099999997690000ULL); + BOOST_CHECK_EQUAL(nSum, 2099999997690000ULL); } bool ReturnFalse() { return false; } diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index d7ea91607c..212be0d2d6 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "consensus/validation.h" #include "main.h" #include "miner.h" #include "pubkey.h" @@ -73,6 +74,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) pblock->nVersion = 1; pblock->nTime = chainActive.Tip()->GetMedianTimePast()+1; CMutableTransaction txCoinbase(pblock->vtx[0]); + txCoinbase.nVersion = 1; txCoinbase.vin[0].scriptSig = CScript(); txCoinbase.vin[0].scriptSig.push_back(blockinfo[i].extranonce); txCoinbase.vin[0].scriptSig.push_back(chainActive.Height()); @@ -83,7 +85,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) pblock->hashMerkleRoot = pblock->BuildMerkleTree(); pblock->nNonce = blockinfo[i].nonce; CValidationState state; - BOOST_CHECK(ProcessNewBlock(state, NULL, pblock)); + BOOST_CHECK(ProcessNewBlock(state, NULL, pblock, true, NULL)); BOOST_CHECK(state.IsValid()); pblock->hashPrevBlock = pblock->GetHash(); } @@ -222,7 +224,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.nLockTime = chainActive.Tip()->nHeight+1; hash = tx.GetHash(); mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); - BOOST_CHECK(!IsFinalTx(tx, chainActive.Tip()->nHeight + 1)); + BOOST_CHECK(!CheckFinalTx(tx)); // time locked tx2.vin.resize(1); @@ -236,7 +238,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx2.nLockTime = chainActive.Tip()->GetMedianTimePast()+1; hash = tx2.GetHash(); mempool.addUnchecked(hash, CTxMemPoolEntry(tx2, 11, GetTime(), 111.0, 11)); - BOOST_CHECK(!IsFinalTx(tx2)); + BOOST_CHECK(!CheckFinalTx(tx2)); BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); @@ -248,8 +250,10 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) chainActive.Tip()->nHeight++; SetMockTime(chainActive.Tip()->GetMedianTimePast()+2); - BOOST_CHECK(IsFinalTx(tx, chainActive.Tip()->nHeight + 1)); - BOOST_CHECK(IsFinalTx(tx2)); + // FIXME: we should *actually* create a new block so the following test + // works; CheckFinalTx() isn't fooled by monkey-patching nHeight. + //BOOST_CHECK(CheckFinalTx(tx)); + //BOOST_CHECK(CheckFinalTx(tx2)); BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3); diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index cb357d295c..0f5e1615c6 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -117,6 +117,11 @@ BOOST_AUTO_TEST_CASE(subnet_test) BOOST_CHECK(CSubNet("1:2:3:4:5:6:7:8").Match(CNetAddr("1:2:3:4:5:6:7:8"))); BOOST_CHECK(!CSubNet("1:2:3:4:5:6:7:8").Match(CNetAddr("1:2:3:4:5:6:7:9"))); BOOST_CHECK(CSubNet("1:2:3:4:5:6:7:0/112").Match(CNetAddr("1:2:3:4:5:6:7:1234"))); + BOOST_CHECK(CSubNet("192.168.0.1/24").Match(CNetAddr("192.168.0.2"))); + BOOST_CHECK(CSubNet("192.168.0.20/29").Match(CNetAddr("192.168.0.18"))); + BOOST_CHECK(CSubNet("1.2.2.1/24").Match(CNetAddr("1.2.2.4"))); + BOOST_CHECK(CSubNet("1.2.2.110/31").Match(CNetAddr("1.2.2.111"))); + BOOST_CHECK(CSubNet("1.2.2.20/26").Match(CNetAddr("1.2.2.63"))); // All-Matching IPv6 Matches arbitrary IPv4 and IPv6 BOOST_CHECK(CSubNet("::/0").Match(CNetAddr("1:2:3:4:5:6:7:1234"))); BOOST_CHECK(CSubNet("::/0").Match(CNetAddr("1.2.3.4"))); diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp new file mode 100644 index 0000000000..cb64ee7c69 --- /dev/null +++ b/src/test/policyestimator_tests.cpp @@ -0,0 +1,186 @@ +// Copyright (c) 2011-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "policy/fees.h" +#include "txmempool.h" +#include "uint256.h" +#include "util.h" + +#include "test/test_bitcoin.h" + +#include <boost/test/unit_test.hpp> + +BOOST_FIXTURE_TEST_SUITE(policyestimator_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) +{ + CTxMemPool mpool(CFeeRate(1000)); + CAmount basefee(2000); + double basepri = 1e6; + CAmount deltaFee(100); + double deltaPri=5e5; + std::vector<CAmount> feeV[2]; + std::vector<double> priV[2]; + + // Populate vectors of increasing fees or priorities + for (int j = 0; j < 10; j++) { + //V[0] is for fee transactions + feeV[0].push_back(basefee * (j+1)); + priV[0].push_back(0); + //V[1] is for priority transactions + feeV[1].push_back(CAmount(0)); + priV[1].push_back(basepri * pow(10, j+1)); + } + + // Store the hashes of transactions that have been + // added to the mempool by their associate fee/pri + // txHashes[j] is populated with transactions either of + // fee = basefee * (j+1) OR pri = 10^6 * 10^(j+1) + std::vector<uint256> txHashes[10]; + + // Create a transaction template + CScript garbage; + for (unsigned int i = 0; i < 128; i++) + garbage.push_back('X'); + CMutableTransaction tx; + std::list<CTransaction> dummyConflicted; + tx.vin.resize(1); + tx.vin[0].scriptSig = garbage; + tx.vout.resize(1); + tx.vout[0].nValue=0LL; + CFeeRate baseRate(basefee, ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)); + + // Create a fake block + std::vector<CTransaction> block; + int blocknum = 0; + + // Loop through 200 blocks + // At a decay .998 and 4 fee transactions per block + // This makes the tx count about 1.33 per bucket, above the 1 threshold + while (blocknum < 200) { + for (int j = 0; j < 10; j++) { // For each fee/pri multiple + for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx + tx.vin[0].prevout.n = 10000*blocknum+100*j+k; // make transaction unique + uint256 hash = tx.GetHash(); + mpool.addUnchecked(hash, CTxMemPoolEntry(tx, feeV[k/4][j], GetTime(), priV[k/4][j], blocknum, mpool.HasNoInputsOf(tx))); + txHashes[j].push_back(hash); + } + } + //Create blocks where higher fee/pri txs are included more often + for (int h = 0; h <= blocknum%10; h++) { + // 10/10 blocks add highest fee/pri transactions + // 9/10 blocks add 2nd highest and so on until ... + // 1/10 blocks add lowest fee/pri transactions + while (txHashes[9-h].size()) { + CTransaction btx; + if (mpool.lookup(txHashes[9-h].back(), btx)) + block.push_back(btx); + txHashes[9-h].pop_back(); + } + } + mpool.removeForBlock(block, ++blocknum, dummyConflicted); + block.clear(); + if (blocknum == 30) { + // At this point we should need to combine 5 buckets to get enough data points + // So estimateFee(1) should fail and estimateFee(2) should return somewhere around + // 8*baserate + BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0)); + BOOST_CHECK(mpool.estimateFee(2).GetFeePerK() < 8*baseRate.GetFeePerK() + deltaFee); + BOOST_CHECK(mpool.estimateFee(2).GetFeePerK() > 8*baseRate.GetFeePerK() - deltaFee); + } + } + + std::vector<CAmount> origFeeEst; + std::vector<double> origPriEst; + // Highest feerate is 10*baseRate and gets in all blocks, + // second highest feerate is 9*baseRate and gets in 9/10 blocks = 90%, + // third highest feerate is 8*base rate, and gets in 8/10 blocks = 80%, + // so estimateFee(1) should return 9*baseRate. + // Third highest feerate has 90% chance of being included by 2 blocks, + // so estimateFee(2) should return 8*baseRate etc... + for (int i = 1; i < 10;i++) { + origFeeEst.push_back(mpool.estimateFee(i).GetFeePerK()); + origPriEst.push_back(mpool.estimatePriority(i)); + if (i > 1) { // Fee estimates should be monotonically decreasing + BOOST_CHECK(origFeeEst[i-1] <= origFeeEst[i-2]); + BOOST_CHECK(origPriEst[i-1] <= origPriEst[i-2]); + } + BOOST_CHECK(origFeeEst[i-1] < (10-i)*baseRate.GetFeePerK() + deltaFee); + BOOST_CHECK(origFeeEst[i-1] > (10-i)*baseRate.GetFeePerK() - deltaFee); + BOOST_CHECK(origPriEst[i-1] < pow(10,10-i) * basepri + deltaPri); + BOOST_CHECK(origPriEst[i-1] > pow(10,10-i) * basepri - deltaPri); + } + + // Mine 50 more blocks with no transactions happening, estimates shouldn't change + // We haven't decayed the moving average enough so we still have enough data points in every bucket + while (blocknum < 250) + mpool.removeForBlock(block, ++blocknum, dummyConflicted); + + for (int i = 1; i < 10;i++) { + BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] + deltaFee); + BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee); + BOOST_CHECK(mpool.estimatePriority(i) < origPriEst[i-1] + deltaPri); + BOOST_CHECK(mpool.estimatePriority(i) > origPriEst[i-1] - deltaPri); + } + + + // Mine 15 more blocks with lots of transactions happening and not getting mined + // Estimates should go up + while (blocknum < 265) { + for (int j = 0; j < 10; j++) { // For each fee/pri multiple + for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx + tx.vin[0].prevout.n = 10000*blocknum+100*j+k; + uint256 hash = tx.GetHash(); + mpool.addUnchecked(hash, CTxMemPoolEntry(tx, feeV[k/4][j], GetTime(), priV[k/4][j], blocknum, mpool.HasNoInputsOf(tx))); + txHashes[j].push_back(hash); + } + } + mpool.removeForBlock(block, ++blocknum, dummyConflicted); + } + + for (int i = 1; i < 10;i++) { + BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee); + BOOST_CHECK(mpool.estimatePriority(i) > origPriEst[i-1] - deltaPri); + } + + // Mine all those transactions + // Estimates should still not be below original + for (int j = 0; j < 10; j++) { + while(txHashes[j].size()) { + CTransaction btx; + if (mpool.lookup(txHashes[j].back(), btx)) + block.push_back(btx); + txHashes[j].pop_back(); + } + } + mpool.removeForBlock(block, 265, dummyConflicted); + block.clear(); + for (int i = 1; i < 10;i++) { + BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee); + BOOST_CHECK(mpool.estimatePriority(i) > origPriEst[i-1] - deltaPri); + } + + // Mine 100 more blocks where everything is mined every block + // Estimates should be below original estimates (not possible for last estimate) + while (blocknum < 365) { + for (int j = 0; j < 10; j++) { // For each fee/pri multiple + for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx + tx.vin[0].prevout.n = 10000*blocknum+100*j+k; + uint256 hash = tx.GetHash(); + mpool.addUnchecked(hash, CTxMemPoolEntry(tx, feeV[k/4][j], GetTime(), priV[k/4][j], blocknum, mpool.HasNoInputsOf(tx))); + CTransaction btx; + if (mpool.lookup(hash, btx)) + block.push_back(btx); + } + } + mpool.removeForBlock(block, ++blocknum, dummyConflicted); + block.clear(); + } + for (int i = 1; i < 9; i++) { + BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee); + BOOST_CHECK(mpool.estimatePriority(i) < origPriEst[i-1] - deltaPri); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 45cb551d04..08f988fdbf 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -13,35 +13,36 @@ #include <boost/algorithm/string.hpp> #include <boost/test/unit_test.hpp> +#include "univalue/univalue.h" + using namespace std; -using namespace json_spirit; -Array +UniValue createArgs(int nRequired, const char* address1=NULL, const char* address2=NULL) { - Array result; + UniValue result(UniValue::VARR); result.push_back(nRequired); - Array addresses; + UniValue addresses(UniValue::VARR); if (address1) addresses.push_back(address1); if (address2) addresses.push_back(address2); result.push_back(addresses); return result; } -Value CallRPC(string args) +UniValue CallRPC(string args) { vector<string> vArgs; boost::split(vArgs, args, boost::is_any_of(" \t")); string strMethod = vArgs[0]; vArgs.erase(vArgs.begin()); - Array params = RPCConvertValues(strMethod, vArgs); + UniValue params = RPCConvertValues(strMethod, vArgs); rpcfn_type method = tableRPC[strMethod]->actor; try { - Value result = (*method)(params, false); + UniValue result = (*method)(params, false); return result; } - catch (const Object& objError) { + catch (const UniValue& objError) { throw runtime_error(find_value(objError, "message").get_str()); } } @@ -52,7 +53,7 @@ BOOST_FIXTURE_TEST_SUITE(rpc_tests, TestingSetup) BOOST_AUTO_TEST_CASE(rpc_rawparams) { // Test raw transaction API argument handling - Value r; + UniValue r; BOOST_CHECK_THROW(CallRPC("getrawtransaction"), runtime_error); BOOST_CHECK_THROW(CallRPC("getrawtransaction not_hex"), runtime_error); @@ -92,7 +93,7 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams) BOOST_AUTO_TEST_CASE(rpc_rawsign) { - Value r; + UniValue r; // input is a 1-of-2 multisig (so is output): string prevout = "[{\"txid\":\"b4cc287e58f87cdae59417329f710f3ecd75a4ee1d2872b7248f50977c8493f3\"," @@ -111,25 +112,28 @@ BOOST_AUTO_TEST_CASE(rpc_rawsign) BOOST_AUTO_TEST_CASE(rpc_format_monetary_values) { - BOOST_CHECK_EQUAL(write_string(ValueFromAmount(0LL), false), "0.00000000"); - BOOST_CHECK_EQUAL(write_string(ValueFromAmount(1LL), false), "0.00000001"); - BOOST_CHECK_EQUAL(write_string(ValueFromAmount(17622195LL), false), "0.17622195"); - BOOST_CHECK_EQUAL(write_string(ValueFromAmount(50000000LL), false), "0.50000000"); - BOOST_CHECK_EQUAL(write_string(ValueFromAmount(89898989LL), false), "0.89898989"); - BOOST_CHECK_EQUAL(write_string(ValueFromAmount(100000000LL), false), "1.00000000"); - BOOST_CHECK_EQUAL(write_string(ValueFromAmount(2099999999999990LL), false), "20999999.99999990"); - BOOST_CHECK_EQUAL(write_string(ValueFromAmount(2099999999999999LL), false), "20999999.99999999"); + BOOST_CHECK(ValueFromAmount(0LL).write() == "0.00000000"); + BOOST_CHECK(ValueFromAmount(1LL).write() == "0.00000001"); + BOOST_CHECK(ValueFromAmount(17622195LL).write() == "0.17622195"); + BOOST_CHECK(ValueFromAmount(50000000LL).write() == "0.50000000"); + BOOST_CHECK(ValueFromAmount(89898989LL).write() == "0.89898989"); + BOOST_CHECK(ValueFromAmount(100000000LL).write() == "1.00000000"); + BOOST_CHECK(ValueFromAmount(2099999999999990LL).write() == "20999999.99999990"); + BOOST_CHECK(ValueFromAmount(2099999999999999LL).write() == "20999999.99999999"); } -static Value ValueFromString(const std::string &str) +static UniValue ValueFromString(const std::string &str) { - Value value; - BOOST_CHECK(read_string(str, value)); + UniValue value; + BOOST_CHECK(value.setNumStr(str)); return value; } BOOST_AUTO_TEST_CASE(rpc_parse_monetary_values) { + BOOST_CHECK_THROW(AmountFromValue(ValueFromString("-0.00000001")), UniValue); + BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0")), 0LL); + BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.00000000")), 0LL); BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.00000001")), 1LL); BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.17622195")), 17622195LL); BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.5")), 50000000LL); @@ -140,6 +144,24 @@ BOOST_AUTO_TEST_CASE(rpc_parse_monetary_values) BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("20999999.99999999")), 2099999999999999LL); } +BOOST_AUTO_TEST_CASE(json_parse_errors) +{ + // Valid + BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("1.0").get_real(), 1.0); + // Valid, with leading or trailing whitespace + BOOST_CHECK_EQUAL(ParseNonRFCJSONValue(" 1.0").get_real(), 1.0); + BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("1.0 ").get_real(), 1.0); + // Invalid, initial garbage + BOOST_CHECK_THROW(ParseNonRFCJSONValue("[1.0"), std::runtime_error); + BOOST_CHECK_THROW(ParseNonRFCJSONValue("a1.0"), std::runtime_error); + // Invalid, trailing garbage + BOOST_CHECK_THROW(ParseNonRFCJSONValue("1.0sds"), std::runtime_error); + BOOST_CHECK_THROW(ParseNonRFCJSONValue("1.0]"), std::runtime_error); + // BTC addresses should fail parsing + BOOST_CHECK_THROW(ParseNonRFCJSONValue("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"), std::runtime_error); + BOOST_CHECK_THROW(ParseNonRFCJSONValue("3J98t1WpEZ73CNmQviecrnyiWrnqRhWNL"), std::runtime_error); +} + BOOST_AUTO_TEST_CASE(rpc_boostasiotocnetaddr) { // Check IPv4 addresses diff --git a/src/test/rpc_wallet_tests.cpp b/src/test/rpc_wallet_tests.cpp index 4d5e92cbd4..a72b656100 100644 --- a/src/test/rpc_wallet_tests.cpp +++ b/src/test/rpc_wallet_tests.cpp @@ -14,11 +14,12 @@ #include <boost/algorithm/string.hpp> #include <boost/test/unit_test.hpp> +#include "univalue/univalue.h" + using namespace std; -using namespace json_spirit; -extern Array createArgs(int nRequired, const char* address1 = NULL, const char* address2 = NULL); -extern Value CallRPC(string args); +extern UniValue createArgs(int nRequired, const char* address1 = NULL, const char* address2 = NULL); +extern UniValue CallRPC(string args); extern CWallet* pwalletMain; @@ -35,7 +36,7 @@ BOOST_AUTO_TEST_CASE(rpc_addmultisig) // new, compressed: const char address2Hex[] = "0388c2037017c62240b6b72ac1a2a5f94da790596ebd06177c8572752922165cb4"; - Value v; + UniValue v; CBitcoinAddress address; BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex), false)); address.SetString(v.get_str()); @@ -66,13 +67,13 @@ BOOST_AUTO_TEST_CASE(rpc_addmultisig) BOOST_AUTO_TEST_CASE(rpc_wallet) { // Test RPC calls for various wallet statistics - Value r; + UniValue r; LOCK2(cs_main, pwalletMain->cs_wallet); CPubKey demoPubkey = pwalletMain->GenerateNewKey(); CBitcoinAddress demoAddress = CBitcoinAddress(CTxDestination(demoPubkey.GetID())); - Value retValue; + UniValue retValue; string strAccount = "walletDemoAccount"; string strPurpose = "receive"; BOOST_CHECK_NO_THROW({ /*Initialize Wallet with an account */ @@ -213,7 +214,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) *********************************/ BOOST_CHECK_THROW(CallRPC("getaddressesbyaccount"), runtime_error); BOOST_CHECK_NO_THROW(retValue = CallRPC("getaddressesbyaccount " + strAccount)); - Array arr = retValue.get_array(); + UniValue arr = retValue.get_array(); BOOST_CHECK(arr.size() > 0); BOOST_CHECK(CBitcoinAddress(arr[0].get_str()).Get() == demoAddress.Get()); } diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp new file mode 100644 index 0000000000..cb1a427db0 --- /dev/null +++ b/src/test/scheduler_tests.cpp @@ -0,0 +1,119 @@ +// Copyright (c) 2012-2013 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "random.h" +#include "scheduler.h" + +#include "test/test_bitcoin.h" + +#include <boost/bind.hpp> +#include <boost/random/mersenne_twister.hpp> +#include <boost/random/uniform_int_distribution.hpp> +#include <boost/thread.hpp> +#include <boost/test/unit_test.hpp> + +BOOST_AUTO_TEST_SUITE(scheduler_tests) + +static void microTask(CScheduler& s, boost::mutex& mutex, int& counter, int delta, boost::chrono::system_clock::time_point rescheduleTime) +{ + { + boost::unique_lock<boost::mutex> lock(mutex); + counter += delta; + } + boost::chrono::system_clock::time_point noTime = boost::chrono::system_clock::time_point::min(); + if (rescheduleTime != noTime) { + CScheduler::Function f = boost::bind(µTask, boost::ref(s), boost::ref(mutex), boost::ref(counter), -delta + 1, noTime); + s.schedule(f, rescheduleTime); + } +} + +static void MicroSleep(uint64_t n) +{ +#if defined(HAVE_WORKING_BOOST_SLEEP_FOR) + boost::this_thread::sleep_for(boost::chrono::microseconds(n)); +#elif defined(HAVE_WORKING_BOOST_SLEEP) + boost::this_thread::sleep(boost::posix_time::microseconds(n)); +#else + //should never get here + #error missing boost sleep implementation +#endif +} + +BOOST_AUTO_TEST_CASE(manythreads) +{ + seed_insecure_rand(false); + + // Stress test: hundreds of microsecond-scheduled tasks, + // serviced by 10 threads. + // + // So... ten shared counters, which if all the tasks execute + // properly will sum to the number of tasks done. + // Each task adds or subtracts from one of the counters a + // random amount, and then schedules another task 0-1000 + // microseconds in the future to subtract or add from + // the counter -random_amount+1, so in the end the shared + // counters should sum to the number of initial tasks performed. + CScheduler microTasks; + + boost::mutex counterMutex[10]; + int counter[10] = { 0 }; + boost::random::mt19937 rng(insecure_rand()); + boost::random::uniform_int_distribution<> zeroToNine(0, 9); + boost::random::uniform_int_distribution<> randomMsec(-11, 1000); + boost::random::uniform_int_distribution<> randomDelta(-1000, 1000); + + boost::chrono::system_clock::time_point start = boost::chrono::system_clock::now(); + boost::chrono::system_clock::time_point now = start; + boost::chrono::system_clock::time_point first, last; + size_t nTasks = microTasks.getQueueInfo(first, last); + BOOST_CHECK(nTasks == 0); + + for (int i = 0; i < 100; i++) { + boost::chrono::system_clock::time_point t = now + boost::chrono::microseconds(randomMsec(rng)); + boost::chrono::system_clock::time_point tReschedule = now + boost::chrono::microseconds(500 + randomMsec(rng)); + int whichCounter = zeroToNine(rng); + CScheduler::Function f = boost::bind(µTask, boost::ref(microTasks), + boost::ref(counterMutex[whichCounter]), boost::ref(counter[whichCounter]), + randomDelta(rng), tReschedule); + microTasks.schedule(f, t); + } + nTasks = microTasks.getQueueInfo(first, last); + BOOST_CHECK(nTasks == 100); + BOOST_CHECK(first < last); + BOOST_CHECK(last > now); + + // As soon as these are created they will start running and servicing the queue + boost::thread_group microThreads; + for (int i = 0; i < 5; i++) + microThreads.create_thread(boost::bind(&CScheduler::serviceQueue, µTasks)); + + MicroSleep(600); + now = boost::chrono::system_clock::now(); + + // More threads and more tasks: + for (int i = 0; i < 5; i++) + microThreads.create_thread(boost::bind(&CScheduler::serviceQueue, µTasks)); + for (int i = 0; i < 100; i++) { + boost::chrono::system_clock::time_point t = now + boost::chrono::microseconds(randomMsec(rng)); + boost::chrono::system_clock::time_point tReschedule = now + boost::chrono::microseconds(500 + randomMsec(rng)); + int whichCounter = zeroToNine(rng); + CScheduler::Function f = boost::bind(µTask, boost::ref(microTasks), + boost::ref(counterMutex[whichCounter]), boost::ref(counter[whichCounter]), + randomDelta(rng), tReschedule); + microTasks.schedule(f, t); + } + + // Drain the task queue then exit threads + microTasks.stop(true); + microThreads.join_all(); // ... wait until all the threads are done + + int counterSum = 0; + for (int i = 0; i < 10; i++) { + BOOST_CHECK(counter[i] != 0); + counterSum += counter[i]; + } + BOOST_CHECK_EQUAL(counterSum, 200); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index c0614cca43..3733425699 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -26,12 +26,10 @@ #include <boost/foreach.hpp> #include <boost/test/unit_test.hpp> -#include "json/json_spirit_reader_template.h" -#include "json/json_spirit_utils.h" -#include "json/json_spirit_writer_template.h" + +#include "univalue/univalue.h" using namespace std; -using namespace json_spirit; // Uncomment if you want to output updated JSON tests. // #define UPDATE_JSON_TESTS @@ -41,15 +39,15 @@ static const unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; unsigned int ParseScriptFlags(string strFlags); string FormatScriptFlags(unsigned int flags); -Array +UniValue read_json(const std::string& jsondata) { - Value v; + UniValue v; - if (!read_string(jsondata, v) || v.type() != array_type) + if (!v.read(jsondata) || !v.isArray()) { BOOST_ERROR("Parse error."); - return Array(); + return UniValue(UniValue::VARR); } return v.get_array(); } @@ -295,10 +293,10 @@ public: return *this; } - Array GetJSON() + UniValue GetJSON() { DoPush(); - Array array; + UniValue array(UniValue::VARR); array.push_back(FormatScript(spendTx.vin[0].scriptSig)); array.push_back(FormatScript(creditTx.vout[0].scriptPubKey)); array.push_back(FormatScriptFlags(flags)); @@ -582,14 +580,16 @@ BOOST_AUTO_TEST_CASE(script_build) std::set<std::string> tests_bad; { - Array json_good = read_json(std::string(json_tests::script_valid, json_tests::script_valid + sizeof(json_tests::script_valid))); - Array json_bad = read_json(std::string(json_tests::script_invalid, json_tests::script_invalid + sizeof(json_tests::script_invalid))); + UniValue json_good = read_json(std::string(json_tests::script_valid, json_tests::script_valid + sizeof(json_tests::script_valid))); + UniValue json_bad = read_json(std::string(json_tests::script_invalid, json_tests::script_invalid + sizeof(json_tests::script_invalid))); - BOOST_FOREACH(Value& tv, json_good) { - tests_good.insert(write_string(Value(tv.get_array()), true)); + for (unsigned int idx = 0; idx < json_good.size(); idx++) { + const UniValue& tv = json_good[idx]; + tests_good.insert(tv.get_array().write()); } - BOOST_FOREACH(Value& tv, json_bad) { - tests_bad.insert(write_string(Value(tv.get_array()), true)); + for (unsigned int idx = 0; idx < json_bad.size(); idx++) { + const UniValue& tv = json_bad[idx]; + tests_bad.insert(tv.get_array().write()); } } @@ -598,7 +598,7 @@ BOOST_AUTO_TEST_CASE(script_build) BOOST_FOREACH(TestBuilder& test, good) { test.Test(true); - std::string str = write_string(Value(test.GetJSON()), true); + std::string str = test.GetJSON().write(); #ifndef UPDATE_JSON_TESTS if (tests_good.count(str) == 0) { BOOST_CHECK_MESSAGE(false, "Missing auto script_valid test: " + test.GetComment()); @@ -608,7 +608,7 @@ BOOST_AUTO_TEST_CASE(script_build) } BOOST_FOREACH(TestBuilder& test, bad) { test.Test(false); - std::string str = write_string(Value(test.GetJSON()), true); + std::string str = test.GetJSON().write(); #ifndef UPDATE_JSON_TESTS if (tests_bad.count(str) == 0) { BOOST_CHECK_MESSAGE(false, "Missing auto script_invalid test: " + test.GetComment()); @@ -634,12 +634,11 @@ BOOST_AUTO_TEST_CASE(script_valid) // Inner arrays are [ "scriptSig", "scriptPubKey", "flags" ] // ... where scriptSig and scriptPubKey are stringified // scripts. - Array tests = read_json(std::string(json_tests::script_valid, json_tests::script_valid + sizeof(json_tests::script_valid))); + UniValue tests = read_json(std::string(json_tests::script_valid, json_tests::script_valid + sizeof(json_tests::script_valid))); - BOOST_FOREACH(Value& tv, tests) - { - Array test = tv.get_array(); - string strTest = write_string(tv, false); + for (unsigned int idx = 0; idx < tests.size(); idx++) { + UniValue test = tests[idx]; + string strTest = test.write(); if (test.size() < 3) // Allow size > 3; extra stuff ignored (useful for comments) { if (test.size() != 1) { @@ -660,13 +659,12 @@ BOOST_AUTO_TEST_CASE(script_valid) BOOST_AUTO_TEST_CASE(script_invalid) { // Scripts that should evaluate as invalid - Array tests = read_json(std::string(json_tests::script_invalid, json_tests::script_invalid + sizeof(json_tests::script_invalid))); + UniValue tests = read_json(std::string(json_tests::script_invalid, json_tests::script_invalid + sizeof(json_tests::script_invalid))); - BOOST_FOREACH(Value& tv, tests) - { - Array test = tv.get_array(); - string strTest = write_string(tv, false); - if (test.size() < 3) // Allow size > 3; extra stuff ignored (useful for comments) + for (unsigned int idx = 0; idx < tests.size(); idx++) { + UniValue test = tests[idx]; + string strTest = test.write(); + if (test.size() < 3) // Allow size > 2; extra stuff ignored (useful for comments) { if (test.size() != 1) { BOOST_ERROR("Bad test: " << strTest); diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index afb7a41bbd..a0797d5f3f 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -2,25 +2,24 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "consensus/validation.h" #include "data/sighash.json.h" #include "main.h" #include "random.h" -#include "serialize.h" -#include "script/script.h" #include "script/interpreter.h" +#include "script/script.h" +#include "serialize.h" +#include "test/test_bitcoin.h" #include "util.h" #include "version.h" -#include "test/test_bitcoin.h" #include <iostream> #include <boost/test/unit_test.hpp> -#include "json/json_spirit_reader_template.h" -#include "json/json_spirit_utils.h" -#include "json/json_spirit_writer_template.h" -using namespace json_spirit; -extern Array read_json(const std::string& jsondata); +#include "univalue/univalue.h" + +extern UniValue read_json(const std::string& jsondata); // Old script.cpp SignatureHash function uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) @@ -167,12 +166,11 @@ BOOST_AUTO_TEST_CASE(sighash_test) // Goal: check that SignatureHash generates correct hash BOOST_AUTO_TEST_CASE(sighash_from_data) { - Array tests = read_json(std::string(json_tests::sighash, json_tests::sighash + sizeof(json_tests::sighash))); + UniValue tests = read_json(std::string(json_tests::sighash, json_tests::sighash + sizeof(json_tests::sighash))); - BOOST_FOREACH(Value& tv, tests) - { - Array test = tv.get_array(); - std::string strTest = write_string(tv, false); + for (unsigned int idx = 0; idx < tests.size(); idx++) { + UniValue test = tests[idx]; + std::string strTest = test.write(); if (test.size() < 1) // Allow for extra stuff (useful for comments) { BOOST_ERROR("Bad test: " << strTest); diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 2a3083316e..4cfdec1267 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -7,27 +7,29 @@ #include "test/test_bitcoin.h" #include "clientversion.h" +#include "consensus/validation.h" +#include "core_io.h" #include "key.h" #include "keystore.h" #include "main.h" #include "script/script.h" #include "script/script_error.h" -#include "core_io.h" #include <map> #include <string> #include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/split.hpp> +#include <boost/assign/list_of.hpp> #include <boost/test/unit_test.hpp> #include <boost/assign/list_of.hpp> -#include "json/json_spirit_writer_template.h" + +#include "univalue/univalue.h" using namespace std; -using namespace json_spirit; // In script_tests.cpp -extern Array read_json(const std::string& jsondata); +extern UniValue read_json(const std::string& jsondata); static std::map<string, unsigned int> mapFlagNames = boost::assign::map_list_of (string("NONE"), (unsigned int)SCRIPT_VERIFY_NONE) @@ -87,32 +89,31 @@ BOOST_AUTO_TEST_CASE(tx_valid) // ... where all scripts are stringified scripts. // // verifyFlags is a comma separated list of script verification flags to apply, or "NONE" - Array tests = read_json(std::string(json_tests::tx_valid, json_tests::tx_valid + sizeof(json_tests::tx_valid))); + UniValue tests = read_json(std::string(json_tests::tx_valid, json_tests::tx_valid + sizeof(json_tests::tx_valid))); ScriptError err; - BOOST_FOREACH(Value& tv, tests) - { - Array test = tv.get_array(); - string strTest = write_string(tv, false); - if (test[0].type() == array_type) + for (unsigned int idx = 0; idx < tests.size(); idx++) { + UniValue test = tests[idx]; + string strTest = test.write(); + if (test[0].isArray()) { - if (test.size() != 3 || test[1].type() != str_type || test[2].type() != str_type) + if (test.size() != 3 || !test[1].isStr() || !test[2].isStr()) { BOOST_ERROR("Bad test: " << strTest); continue; } map<COutPoint, CScript> mapprevOutScriptPubKeys; - Array inputs = test[0].get_array(); + UniValue inputs = test[0].get_array(); bool fValid = true; - BOOST_FOREACH(Value& input, inputs) - { - if (input.type() != array_type) + for (unsigned int inpIdx = 0; inpIdx < inputs.size(); inpIdx++) { + const UniValue& input = inputs[inpIdx]; + if (!input.isArray()) { fValid = false; break; } - Array vinput = input.get_array(); + UniValue vinput = input.get_array(); if (vinput.size() != 3) { fValid = false; @@ -163,32 +164,31 @@ BOOST_AUTO_TEST_CASE(tx_invalid) // ... where all scripts are stringified scripts. // // verifyFlags is a comma separated list of script verification flags to apply, or "NONE" - Array tests = read_json(std::string(json_tests::tx_invalid, json_tests::tx_invalid + sizeof(json_tests::tx_invalid))); + UniValue tests = read_json(std::string(json_tests::tx_invalid, json_tests::tx_invalid + sizeof(json_tests::tx_invalid))); ScriptError err; - BOOST_FOREACH(Value& tv, tests) - { - Array test = tv.get_array(); - string strTest = write_string(tv, false); - if (test[0].type() == array_type) + for (unsigned int idx = 0; idx < tests.size(); idx++) { + UniValue test = tests[idx]; + string strTest = test.write(); + if (test[0].isArray()) { - if (test.size() != 3 || test[1].type() != str_type || test[2].type() != str_type) + if (test.size() != 3 || !test[1].isStr() || !test[2].isStr()) { BOOST_ERROR("Bad test: " << strTest); continue; } map<COutPoint, CScript> mapprevOutScriptPubKeys; - Array inputs = test[0].get_array(); + UniValue inputs = test[0].get_array(); bool fValid = true; - BOOST_FOREACH(Value& input, inputs) - { - if (input.type() != array_type) + for (unsigned int inpIdx = 0; inpIdx < inputs.size(); inpIdx++) { + const UniValue& input = inputs[inpIdx]; + if (!input.isArray()) { fValid = false; break; } - Array vinput = input.get_array(); + UniValue vinput = input.get_array(); if (vinput.size() != 3) { fValid = false; diff --git a/src/test/univalue_tests.cpp b/src/test/univalue_tests.cpp index 8cecfbf651..de84faca23 100644 --- a/src/test/univalue_tests.cpp +++ b/src/test/univalue_tests.cpp @@ -49,7 +49,7 @@ BOOST_AUTO_TEST_CASE(univalue_constructor) double vd = -7.21; UniValue v7(vd); - BOOST_CHECK(v7.isNum()); + BOOST_CHECK(v7.isReal()); BOOST_CHECK_EQUAL(v7.getValStr(), "-7.21"); string vs("yawn"); @@ -63,6 +63,48 @@ BOOST_AUTO_TEST_CASE(univalue_constructor) BOOST_CHECK_EQUAL(v9.getValStr(), "zappa"); } +BOOST_AUTO_TEST_CASE(univalue_typecheck) +{ + UniValue v1; + BOOST_CHECK(v1.setNumStr("1")); + BOOST_CHECK(v1.isNum()); + BOOST_CHECK_THROW(v1.get_bool(), runtime_error); + + UniValue v2; + BOOST_CHECK(v2.setBool(true)); + BOOST_CHECK_EQUAL(v2.get_bool(), true); + BOOST_CHECK_THROW(v2.get_int(), runtime_error); + + UniValue v3; + BOOST_CHECK(v3.setNumStr("32482348723847471234")); + BOOST_CHECK_THROW(v3.get_int64(), runtime_error); + BOOST_CHECK(v3.setNumStr("1000")); + BOOST_CHECK_EQUAL(v3.get_int64(), 1000); + + UniValue v4; + BOOST_CHECK(v4.setNumStr("2147483648")); + BOOST_CHECK_EQUAL(v4.get_int64(), 2147483648); + BOOST_CHECK_THROW(v4.get_int(), runtime_error); + BOOST_CHECK(v4.setNumStr("1000")); + BOOST_CHECK_EQUAL(v4.get_int(), 1000); + BOOST_CHECK_THROW(v4.get_str(), runtime_error); + BOOST_CHECK_EQUAL(v4.get_real(), 1000); + BOOST_CHECK_THROW(v4.get_array(), runtime_error); + BOOST_CHECK_THROW(v4.getKeys(), runtime_error); + BOOST_CHECK_THROW(v4.getValues(), runtime_error); + BOOST_CHECK_THROW(v4.get_obj(), runtime_error); + + UniValue v5; + BOOST_CHECK(v5.read("[true, 10]")); + BOOST_CHECK_NO_THROW(v5.get_array()); + std::vector<UniValue> vals = v5.getValues(); + BOOST_CHECK_THROW(vals[0].get_int(), runtime_error); + BOOST_CHECK_EQUAL(vals[0].get_bool(), true); + + BOOST_CHECK_EQUAL(vals[1].get_int(), 10); + BOOST_CHECK_THROW(vals[1].get_bool(), runtime_error); +} + BOOST_AUTO_TEST_CASE(univalue_set) { UniValue v(UniValue::VSTR, "foo"); @@ -72,20 +114,20 @@ BOOST_AUTO_TEST_CASE(univalue_set) BOOST_CHECK(v.setObject()); BOOST_CHECK(v.isObject()); - BOOST_CHECK_EQUAL(v.count(), 0); + BOOST_CHECK_EQUAL(v.size(), 0); BOOST_CHECK_EQUAL(v.getType(), UniValue::VOBJ); BOOST_CHECK(v.empty()); BOOST_CHECK(v.setArray()); BOOST_CHECK(v.isArray()); - BOOST_CHECK_EQUAL(v.count(), 0); + BOOST_CHECK_EQUAL(v.size(), 0); BOOST_CHECK(v.setStr("zum")); BOOST_CHECK(v.isStr()); BOOST_CHECK_EQUAL(v.getValStr(), "zum"); BOOST_CHECK(v.setFloat(-1.01)); - BOOST_CHECK(v.isNum()); + BOOST_CHECK(v.isReal()); BOOST_CHECK_EQUAL(v.getValStr(), "-1.01"); BOOST_CHECK(v.setInt((int)1023)); @@ -145,7 +187,7 @@ BOOST_AUTO_TEST_CASE(univalue_array) BOOST_CHECK(arr.push_backV(vec)); BOOST_CHECK_EQUAL(arr.empty(), false); - BOOST_CHECK_EQUAL(arr.count(), 5); + BOOST_CHECK_EQUAL(arr.size(), 5); BOOST_CHECK_EQUAL(arr[0].getValStr(), "1023"); BOOST_CHECK_EQUAL(arr[1].getValStr(), "zippy"); @@ -157,7 +199,7 @@ BOOST_AUTO_TEST_CASE(univalue_array) arr.clear(); BOOST_CHECK(arr.empty()); - BOOST_CHECK_EQUAL(arr.count(), 0); + BOOST_CHECK_EQUAL(arr.size(), 0); } BOOST_AUTO_TEST_CASE(univalue_object) @@ -197,7 +239,7 @@ BOOST_AUTO_TEST_CASE(univalue_object) BOOST_CHECK(obj.pushKVs(obj2)); BOOST_CHECK_EQUAL(obj.empty(), false); - BOOST_CHECK_EQUAL(obj.count(), 9); + BOOST_CHECK_EQUAL(obj.size(), 9); BOOST_CHECK_EQUAL(obj["age"].getValStr(), "100"); BOOST_CHECK_EQUAL(obj["first"].getValStr(), "John"); @@ -230,7 +272,7 @@ BOOST_AUTO_TEST_CASE(univalue_object) objTypes["distance"] = UniValue::VNUM; objTypes["time"] = UniValue::VNUM; objTypes["calories"] = UniValue::VNUM; - objTypes["temperature"] = UniValue::VNUM; + objTypes["temperature"] = UniValue::VREAL; objTypes["cat1"] = UniValue::VNUM; objTypes["cat2"] = UniValue::VNUM; BOOST_CHECK(obj.checkObject(objTypes)); @@ -240,11 +282,11 @@ BOOST_AUTO_TEST_CASE(univalue_object) obj.clear(); BOOST_CHECK(obj.empty()); - BOOST_CHECK_EQUAL(obj.count(), 0); + BOOST_CHECK_EQUAL(obj.size(), 0); } static const char *json1 = -"[1.1,{\"key1\":\"str\",\"key2\":800,\"key3\":{\"name\":\"martian\"}}]"; +"[1.10000000,{\"key1\":\"str\",\"key2\":800,\"key3\":{\"name\":\"martian\"}}]"; BOOST_AUTO_TEST_CASE(univalue_readwrite) { @@ -255,13 +297,13 @@ BOOST_AUTO_TEST_CASE(univalue_readwrite) BOOST_CHECK(v.read(strJson1)); BOOST_CHECK(v.isArray()); - BOOST_CHECK_EQUAL(v.count(), 2); + BOOST_CHECK_EQUAL(v.size(), 2); - BOOST_CHECK_EQUAL(v[0].getValStr(), "1.1"); + BOOST_CHECK_EQUAL(v[0].getValStr(), "1.10000000"); UniValue obj = v[1]; BOOST_CHECK(obj.isObject()); - BOOST_CHECK_EQUAL(obj.count(), 3); + BOOST_CHECK_EQUAL(obj.size(), 3); BOOST_CHECK(obj["key1"].isStr()); BOOST_CHECK_EQUAL(obj["key1"].getValStr(), "str"); diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 3309e2e387..5cb5894251 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -146,29 +146,27 @@ BOOST_AUTO_TEST_CASE(util_GetArg) BOOST_AUTO_TEST_CASE(util_FormatMoney) { - BOOST_CHECK_EQUAL(FormatMoney(0, false), "0.00"); - BOOST_CHECK_EQUAL(FormatMoney((COIN/10000)*123456789, false), "12345.6789"); - BOOST_CHECK_EQUAL(FormatMoney(COIN, true), "+1.00"); - BOOST_CHECK_EQUAL(FormatMoney(-COIN, false), "-1.00"); - BOOST_CHECK_EQUAL(FormatMoney(-COIN, true), "-1.00"); - - BOOST_CHECK_EQUAL(FormatMoney(COIN*100000000, false), "100000000.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN*10000000, false), "10000000.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN*1000000, false), "1000000.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN*100000, false), "100000.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN*10000, false), "10000.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN*1000, false), "1000.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN*100, false), "100.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN*10, false), "10.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN, false), "1.00"); - BOOST_CHECK_EQUAL(FormatMoney(COIN/10, false), "0.10"); - BOOST_CHECK_EQUAL(FormatMoney(COIN/100, false), "0.01"); - BOOST_CHECK_EQUAL(FormatMoney(COIN/1000, false), "0.001"); - BOOST_CHECK_EQUAL(FormatMoney(COIN/10000, false), "0.0001"); - BOOST_CHECK_EQUAL(FormatMoney(COIN/100000, false), "0.00001"); - BOOST_CHECK_EQUAL(FormatMoney(COIN/1000000, false), "0.000001"); - BOOST_CHECK_EQUAL(FormatMoney(COIN/10000000, false), "0.0000001"); - BOOST_CHECK_EQUAL(FormatMoney(COIN/100000000, false), "0.00000001"); + BOOST_CHECK_EQUAL(FormatMoney(0), "0.00"); + BOOST_CHECK_EQUAL(FormatMoney((COIN/10000)*123456789), "12345.6789"); + BOOST_CHECK_EQUAL(FormatMoney(-COIN), "-1.00"); + + BOOST_CHECK_EQUAL(FormatMoney(COIN*100000000), "100000000.00"); + BOOST_CHECK_EQUAL(FormatMoney(COIN*10000000), "10000000.00"); + BOOST_CHECK_EQUAL(FormatMoney(COIN*1000000), "1000000.00"); + BOOST_CHECK_EQUAL(FormatMoney(COIN*100000), "100000.00"); + BOOST_CHECK_EQUAL(FormatMoney(COIN*10000), "10000.00"); + BOOST_CHECK_EQUAL(FormatMoney(COIN*1000), "1000.00"); + BOOST_CHECK_EQUAL(FormatMoney(COIN*100), "100.00"); + BOOST_CHECK_EQUAL(FormatMoney(COIN*10), "10.00"); + BOOST_CHECK_EQUAL(FormatMoney(COIN), "1.00"); + BOOST_CHECK_EQUAL(FormatMoney(COIN/10), "0.10"); + BOOST_CHECK_EQUAL(FormatMoney(COIN/100), "0.01"); + BOOST_CHECK_EQUAL(FormatMoney(COIN/1000), "0.001"); + BOOST_CHECK_EQUAL(FormatMoney(COIN/10000), "0.0001"); + BOOST_CHECK_EQUAL(FormatMoney(COIN/100000), "0.00001"); + BOOST_CHECK_EQUAL(FormatMoney(COIN/1000000), "0.000001"); + BOOST_CHECK_EQUAL(FormatMoney(COIN/10000000), "0.0000001"); + BOOST_CHECK_EQUAL(FormatMoney(COIN/100000000), "0.00000001"); } BOOST_AUTO_TEST_CASE(util_ParseMoney) @@ -322,9 +320,16 @@ BOOST_AUTO_TEST_CASE(test_ParseInt32) BOOST_CHECK(ParseInt32("-2147483648", &n) && n == -2147483648); BOOST_CHECK(ParseInt32("-1234", &n) && n == -1234); // Invalid values + BOOST_CHECK(!ParseInt32("", &n)); + BOOST_CHECK(!ParseInt32(" 1", &n)); // no padding inside + BOOST_CHECK(!ParseInt32("1 ", &n)); BOOST_CHECK(!ParseInt32("1a", &n)); BOOST_CHECK(!ParseInt32("aap", &n)); BOOST_CHECK(!ParseInt32("0x1", &n)); // no hex + BOOST_CHECK(!ParseInt32("0x1", &n)); // no hex + const char test_bytes[] = {'1', 0, '1'}; + std::string teststr(test_bytes, sizeof(test_bytes)); + BOOST_CHECK(!ParseInt32(teststr, &n)); // no embedded NULs // Overflow and underflow BOOST_CHECK(!ParseInt32("-2147483649", NULL)); BOOST_CHECK(!ParseInt32("2147483648", NULL)); @@ -332,6 +337,64 @@ BOOST_AUTO_TEST_CASE(test_ParseInt32) BOOST_CHECK(!ParseInt32("32482348723847471234", NULL)); } +BOOST_AUTO_TEST_CASE(test_ParseInt64) +{ + int64_t n; + // Valid values + BOOST_CHECK(ParseInt64("1234", NULL)); + BOOST_CHECK(ParseInt64("0", &n) && n == 0LL); + BOOST_CHECK(ParseInt64("1234", &n) && n == 1234LL); + BOOST_CHECK(ParseInt64("01234", &n) && n == 1234LL); // no octal + BOOST_CHECK(ParseInt64("2147483647", &n) && n == 2147483647LL); + BOOST_CHECK(ParseInt64("-2147483648", &n) && n == -2147483648LL); + BOOST_CHECK(ParseInt64("9223372036854775807", &n) && n == (int64_t)9223372036854775807); + BOOST_CHECK(ParseInt64("-9223372036854775808", &n) && n == (int64_t)-9223372036854775807-1); + BOOST_CHECK(ParseInt64("-1234", &n) && n == -1234LL); + // Invalid values + BOOST_CHECK(!ParseInt64("", &n)); + BOOST_CHECK(!ParseInt64(" 1", &n)); // no padding inside + BOOST_CHECK(!ParseInt64("1 ", &n)); + BOOST_CHECK(!ParseInt64("1a", &n)); + BOOST_CHECK(!ParseInt64("aap", &n)); + BOOST_CHECK(!ParseInt64("0x1", &n)); // no hex + const char test_bytes[] = {'1', 0, '1'}; + std::string teststr(test_bytes, sizeof(test_bytes)); + BOOST_CHECK(!ParseInt64(teststr, &n)); // no embedded NULs + // Overflow and underflow + BOOST_CHECK(!ParseInt64("-9223372036854775809", NULL)); + BOOST_CHECK(!ParseInt64("9223372036854775808", NULL)); + BOOST_CHECK(!ParseInt64("-32482348723847471234", NULL)); + BOOST_CHECK(!ParseInt64("32482348723847471234", NULL)); +} + +BOOST_AUTO_TEST_CASE(test_ParseDouble) +{ + double n; + // Valid values + BOOST_CHECK(ParseDouble("1234", NULL)); + BOOST_CHECK(ParseDouble("0", &n) && n == 0.0); + BOOST_CHECK(ParseDouble("1234", &n) && n == 1234.0); + BOOST_CHECK(ParseDouble("01234", &n) && n == 1234.0); // no octal + BOOST_CHECK(ParseDouble("2147483647", &n) && n == 2147483647.0); + BOOST_CHECK(ParseDouble("-2147483648", &n) && n == -2147483648.0); + BOOST_CHECK(ParseDouble("-1234", &n) && n == -1234.0); + BOOST_CHECK(ParseDouble("1e6", &n) && n == 1e6); + BOOST_CHECK(ParseDouble("-1e6", &n) && n == -1e6); + // Invalid values + BOOST_CHECK(!ParseDouble("", &n)); + BOOST_CHECK(!ParseDouble(" 1", &n)); // no padding inside + BOOST_CHECK(!ParseDouble("1 ", &n)); + BOOST_CHECK(!ParseDouble("1a", &n)); + BOOST_CHECK(!ParseDouble("aap", &n)); + BOOST_CHECK(!ParseDouble("0x1", &n)); // no hex + const char test_bytes[] = {'1', 0, '1'}; + std::string teststr(test_bytes, sizeof(test_bytes)); + BOOST_CHECK(!ParseDouble(teststr, &n)); // no embedded NULs + // Overflow and underflow + BOOST_CHECK(!ParseDouble("-1e10000", NULL)); + BOOST_CHECK(!ParseDouble("1e10000", NULL)); +} + BOOST_AUTO_TEST_CASE(test_FormatParagraph) { BOOST_CHECK_EQUAL(FormatParagraph("", 79, 0), ""); diff --git a/src/txdb.h b/src/txdb.h index 86e1c5d831..bef5dc9fd1 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -22,7 +22,7 @@ class uint256; //! -dbcache default (MiB) static const int64_t nDefaultDbCache = 100; //! max. -dbcache in (MiB) -static const int64_t nMaxDbCache = sizeof(void*) > 4 ? 4096 : 1024; +static const int64_t nMaxDbCache = sizeof(void*) > 4 ? 16384 : 1024; //! min. -dbcache in (MiB) static const int64_t nMinDbCache = 4; diff --git a/src/txmempool.cpp b/src/txmempool.cpp index d05e3c6eec..1c16e2092e 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -7,29 +7,29 @@ #include "clientversion.h" #include "consensus/consensus.h" +#include "consensus/validation.h" #include "main.h" +#include "policy/fees.h" #include "streams.h" #include "util.h" #include "utilmoneystr.h" #include "version.h" -#include <boost/circular_buffer.hpp> - using namespace std; CTxMemPoolEntry::CTxMemPoolEntry(): - nFee(0), nTxSize(0), nModSize(0), nTime(0), dPriority(0.0) + nFee(0), nTxSize(0), nModSize(0), nTime(0), dPriority(0.0), hadNoDependencies(false) { nHeight = MEMPOOL_HEIGHT; } CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, int64_t _nTime, double _dPriority, - unsigned int _nHeight): - tx(_tx), nFee(_nFee), nTime(_nTime), dPriority(_dPriority), nHeight(_nHeight) + unsigned int _nHeight, bool poolHasNoInputsOf): + tx(_tx), nFee(_nFee), nTime(_nTime), dPriority(_dPriority), nHeight(_nHeight), + hadNoDependencies(poolHasNoInputsOf) { nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - nModSize = tx.CalculateModifiedSize(nTxSize); } @@ -47,346 +47,15 @@ CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const return dResult; } -/** - * Keep track of fee/priority for transactions confirmed within N blocks - */ -class CBlockAverage -{ -private: - boost::circular_buffer<CFeeRate> feeSamples; - boost::circular_buffer<double> prioritySamples; - - template<typename T> std::vector<T> buf2vec(boost::circular_buffer<T> buf) const - { - std::vector<T> vec(buf.begin(), buf.end()); - return vec; - } - -public: - CBlockAverage() : feeSamples(100), prioritySamples(100) { } - - void RecordFee(const CFeeRate& feeRate) { - feeSamples.push_back(feeRate); - } - - void RecordPriority(double priority) { - prioritySamples.push_back(priority); - } - - size_t FeeSamples() const { return feeSamples.size(); } - size_t GetFeeSamples(std::vector<CFeeRate>& insertInto) const - { - BOOST_FOREACH(const CFeeRate& f, feeSamples) - insertInto.push_back(f); - return feeSamples.size(); - } - size_t PrioritySamples() const { return prioritySamples.size(); } - size_t GetPrioritySamples(std::vector<double>& insertInto) const - { - BOOST_FOREACH(double d, prioritySamples) - insertInto.push_back(d); - return prioritySamples.size(); - } - - /** - * Used as belt-and-suspenders check when reading to detect - * file corruption - */ - static bool AreSane(const CFeeRate fee, const CFeeRate& minRelayFee) - { - if (fee < CFeeRate(0)) - return false; - if (fee.GetFeePerK() > minRelayFee.GetFeePerK() * 10000) - return false; - return true; - } - static bool AreSane(const std::vector<CFeeRate>& vecFee, const CFeeRate& minRelayFee) - { - BOOST_FOREACH(CFeeRate fee, vecFee) - { - if (!AreSane(fee, minRelayFee)) - return false; - } - return true; - } - static bool AreSane(const double priority) - { - return priority >= 0; - } - static bool AreSane(const std::vector<double> vecPriority) - { - BOOST_FOREACH(double priority, vecPriority) - { - if (!AreSane(priority)) - return false; - } - return true; - } - - void Write(CAutoFile& fileout) const - { - std::vector<CFeeRate> vecFee = buf2vec(feeSamples); - fileout << vecFee; - std::vector<double> vecPriority = buf2vec(prioritySamples); - fileout << vecPriority; - } - - void Read(CAutoFile& filein, const CFeeRate& minRelayFee) { - std::vector<CFeeRate> vecFee; - filein >> vecFee; - if (AreSane(vecFee, minRelayFee)) - feeSamples.insert(feeSamples.end(), vecFee.begin(), vecFee.end()); - else - throw runtime_error("Corrupt fee value in estimates file."); - std::vector<double> vecPriority; - filein >> vecPriority; - if (AreSane(vecPriority)) - prioritySamples.insert(prioritySamples.end(), vecPriority.begin(), vecPriority.end()); - else - throw runtime_error("Corrupt priority value in estimates file."); - if (feeSamples.size() + prioritySamples.size() > 0) - LogPrint("estimatefee", "Read %d fee samples and %d priority samples\n", - feeSamples.size(), prioritySamples.size()); - } -}; - -class CMinerPolicyEstimator -{ -private: - /** - * Records observed averages transactions that confirmed within one block, two blocks, - * three blocks etc. - */ - std::vector<CBlockAverage> history; - std::vector<CFeeRate> sortedFeeSamples; - std::vector<double> sortedPrioritySamples; - - int nBestSeenHeight; - - /** - * nBlocksAgo is 0 based, i.e. transactions that confirmed in the highest seen block are - * nBlocksAgo == 0, transactions in the block before that are nBlocksAgo == 1 etc. - */ - void seenTxConfirm(const CFeeRate& feeRate, const CFeeRate& minRelayFee, double dPriority, int nBlocksAgo) - { - // Last entry records "everything else". - int nBlocksTruncated = min(nBlocksAgo, (int) history.size() - 1); - assert(nBlocksTruncated >= 0); - - // We need to guess why the transaction was included in a block-- either - // because it is high-priority or because it has sufficient fees. - bool sufficientFee = (feeRate > minRelayFee); - bool sufficientPriority = AllowFree(dPriority); - const char* assignedTo = "unassigned"; - if (sufficientFee && !sufficientPriority && CBlockAverage::AreSane(feeRate, minRelayFee)) - { - history[nBlocksTruncated].RecordFee(feeRate); - assignedTo = "fee"; - } - else if (sufficientPriority && !sufficientFee && CBlockAverage::AreSane(dPriority)) - { - history[nBlocksTruncated].RecordPriority(dPriority); - assignedTo = "priority"; - } - else - { - // Neither or both fee and priority sufficient to get confirmed: - // don't know why they got confirmed. - } - LogPrint("estimatefee", "Seen TX confirm: %s: %s fee/%g priority, took %d blocks\n", - assignedTo, feeRate.ToString(), dPriority, nBlocksAgo); - } - -public: - CMinerPolicyEstimator(int nEntries) : nBestSeenHeight(0) - { - history.resize(nEntries); - } - - void seenBlock(const std::vector<CTxMemPoolEntry>& entries, int nBlockHeight, const CFeeRate minRelayFee) - { - if (nBlockHeight <= nBestSeenHeight) - { - // Ignore side chains and re-orgs; assuming they are random - // they don't affect the estimate. - // And if an attacker can re-org the chain at will, then - // you've got much bigger problems than "attacker can influence - // transaction fees." - return; - } - nBestSeenHeight = nBlockHeight; - - // Fill up the history buckets based on how long transactions took - // to confirm. - std::vector<std::vector<const CTxMemPoolEntry*> > entriesByConfirmations; - entriesByConfirmations.resize(history.size()); - BOOST_FOREACH(const CTxMemPoolEntry& entry, entries) - { - // How many blocks did it take for miners to include this transaction? - int delta = nBlockHeight - entry.GetHeight(); - if (delta <= 0) - { - // Re-org made us lose height, this should only happen if we happen - // to re-org on a difficulty transition point: very rare! - continue; - } - if ((delta-1) >= (int)history.size()) - delta = history.size(); // Last bucket is catch-all - entriesByConfirmations.at(delta-1).push_back(&entry); - } - for (size_t i = 0; i < entriesByConfirmations.size(); i++) - { - std::vector<const CTxMemPoolEntry*> &e = entriesByConfirmations.at(i); - // Insert at most 10 random entries per bucket, otherwise a single block - // can dominate an estimate: - if (e.size() > 10) { - std::random_shuffle(e.begin(), e.end()); - e.resize(10); - } - BOOST_FOREACH(const CTxMemPoolEntry* entry, e) - { - // Fees are stored and reported as BTC-per-kb: - CFeeRate feeRate(entry->GetFee(), entry->GetTxSize()); - double dPriority = entry->GetPriority(entry->GetHeight()); // Want priority when it went IN - seenTxConfirm(feeRate, minRelayFee, dPriority, i); - } - } - - // After new samples are added, we have to clear the sorted lists, - // so they'll be resorted the next time someone asks for an estimate - sortedFeeSamples.clear(); - sortedPrioritySamples.clear(); - - for (size_t i = 0; i < history.size(); i++) { - if (history[i].FeeSamples() + history[i].PrioritySamples() > 0) - LogPrint("estimatefee", "estimates: for confirming within %d blocks based on %d/%d samples, fee=%s, prio=%g\n", - i, - history[i].FeeSamples(), history[i].PrioritySamples(), - estimateFee(i+1).ToString(), estimatePriority(i+1)); - } - } - - /** - * Can return CFeeRate(0) if we don't have any data for that many blocks back. nBlocksToConfirm is 1 based. - */ - CFeeRate estimateFee(int nBlocksToConfirm) - { - nBlocksToConfirm--; - - if (nBlocksToConfirm < 0 || nBlocksToConfirm >= (int)history.size()) - return CFeeRate(0); - - if (sortedFeeSamples.size() == 0) - { - for (size_t i = 0; i < history.size(); i++) - history.at(i).GetFeeSamples(sortedFeeSamples); - std::sort(sortedFeeSamples.begin(), sortedFeeSamples.end(), - std::greater<CFeeRate>()); - } - if (sortedFeeSamples.size() < 11) - { - // Eleven is Gavin's Favorite Number - // ... but we also take a maximum of 10 samples per block so eleven means - // we're getting samples from at least two different blocks - return CFeeRate(0); - } - - int nBucketSize = history.at(nBlocksToConfirm).FeeSamples(); - - // Estimates should not increase as number of confirmations goes up, - // but the estimates are noisy because confirmations happen discretely - // in blocks. To smooth out the estimates, use all samples in the history - // and use the nth highest where n is (number of samples in previous bucket + - // half the samples in nBlocksToConfirm bucket): - size_t nPrevSize = 0; - for (int i = 0; i < nBlocksToConfirm; i++) - nPrevSize += history.at(i).FeeSamples(); - size_t index = min(nPrevSize + nBucketSize/2, sortedFeeSamples.size()-1); - return sortedFeeSamples[index]; - } - double estimatePriority(int nBlocksToConfirm) - { - nBlocksToConfirm--; - - if (nBlocksToConfirm < 0 || nBlocksToConfirm >= (int)history.size()) - return -1; - - if (sortedPrioritySamples.size() == 0) - { - for (size_t i = 0; i < history.size(); i++) - history.at(i).GetPrioritySamples(sortedPrioritySamples); - std::sort(sortedPrioritySamples.begin(), sortedPrioritySamples.end(), - std::greater<double>()); - } - if (sortedPrioritySamples.size() < 11) - return -1.0; - - int nBucketSize = history.at(nBlocksToConfirm).PrioritySamples(); - - // Estimates should not increase as number of confirmations needed goes up, - // but the estimates are noisy because confirmations happen discretely - // in blocks. To smooth out the estimates, use all samples in the history - // and use the nth highest where n is (number of samples in previous buckets + - // half the samples in nBlocksToConfirm bucket). - size_t nPrevSize = 0; - for (int i = 0; i < nBlocksToConfirm; i++) - nPrevSize += history.at(i).PrioritySamples(); - size_t index = min(nPrevSize + nBucketSize/2, sortedPrioritySamples.size()-1); - return sortedPrioritySamples[index]; - } - - void Write(CAutoFile& fileout) const - { - fileout << nBestSeenHeight; - fileout << (uint32_t)history.size(); - BOOST_FOREACH(const CBlockAverage& entry, history) - { - entry.Write(fileout); - } - } - - void Read(CAutoFile& filein, const CFeeRate& minRelayFee) - { - int nFileBestSeenHeight; - filein >> nFileBestSeenHeight; - uint32_t numEntries; - filein >> numEntries; - if (numEntries <= 0 || numEntries > 10000) - throw runtime_error("Corrupt estimates file. Must have between 1 and 10k entries."); - - std::vector<CBlockAverage> fileHistory; - - for (size_t i = 0; i < numEntries; i++) - { - CBlockAverage entry; - entry.Read(filein, minRelayFee); - fileHistory.push_back(entry); - } - - // Now that we've processed the entire fee estimate data file and not - // thrown any errors, we can copy it to our history - nBestSeenHeight = nFileBestSeenHeight; - history = fileHistory; - assert(history.size() > 0); - } -}; - - CTxMemPool::CTxMemPool(const CFeeRate& _minRelayFee) : - nTransactionsUpdated(0), - minRelayFee(_minRelayFee) + nTransactionsUpdated(0) { // Sanity checks off by default for performance, because otherwise // accepting transactions becomes O(N^2) where N is the number // of transactions in the pool fSanityCheck = false; - // 25 blocks is a compromise between using a lot of disk/memory and - // trying to give accurate estimates to people who might be willing - // to wait a day or two to save a fraction of a penny in fees. - // Confirmation times for very-low-fee transactions that take more - // than an hour or three to confirm are highly variable. - minerPolicyEstimator = new CMinerPolicyEstimator(25); + minerPolicyEstimator = new CBlockPolicyEstimator(_minRelayFee); } CTxMemPool::~CTxMemPool() @@ -420,20 +89,20 @@ void CTxMemPool::AddTransactionsUpdated(unsigned int n) } -bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry) +bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate) { // Add to memory pool without checking anything. // Used by main.cpp AcceptToMemoryPool(), which DOES do // all the appropriate checks. LOCK(cs); - { - mapTx[hash] = entry; - const CTransaction& tx = mapTx[hash].GetTx(); - for (unsigned int i = 0; i < tx.vin.size(); i++) - mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i); - nTransactionsUpdated++; - totalTxSize += entry.GetTxSize(); - } + mapTx[hash] = entry; + const CTransaction& tx = mapTx[hash].GetTx(); + for (unsigned int i = 0; i < tx.vin.size(); i++) + mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i); + nTransactionsUpdated++; + totalTxSize += entry.GetTxSize(); + minerPolicyEstimator->processTransaction(entry, fCurrentEstimate); + return true; } @@ -479,6 +148,7 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list<CTransaction>& rem totalTxSize -= mapTx[hash].GetTxSize(); mapTx.erase(hash); nTransactionsUpdated++; + minerPolicyEstimator->removeTx(hash); } } } @@ -529,7 +199,7 @@ void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction> * Called when a block is connected. Removes from mempool and updates the miner fee estimator. */ void CTxMemPool::removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight, - std::list<CTransaction>& conflicts) + std::list<CTransaction>& conflicts, bool fCurrentEstimate) { LOCK(cs); std::vector<CTxMemPoolEntry> entries; @@ -539,7 +209,6 @@ void CTxMemPool::removeForBlock(const std::vector<CTransaction>& vtx, unsigned i if (mapTx.count(hash)) entries.push_back(mapTx[hash]); } - minerPolicyEstimator->seenBlock(entries, nBlockHeight, minRelayFee); BOOST_FOREACH(const CTransaction& tx, vtx) { std::list<CTransaction> dummy; @@ -547,9 +216,10 @@ void CTxMemPool::removeForBlock(const std::vector<CTransaction>& vtx, unsigned i removeConflicts(tx, conflicts); ClearPrioritisation(tx.GetHash()); } + // After the txs in the new block have been removed from the mempool, update policy estimates + minerPolicyEstimator->processBlock(nBlockHeight, entries, fCurrentEstimate); } - void CTxMemPool::clear() { LOCK(cs); @@ -666,7 +336,7 @@ CTxMemPool::WriteFeeEstimates(CAutoFile& fileout) const { try { LOCK(cs); - fileout << 99900; // version required to read: 0.9.99 or later + fileout << 109900; // version required to read: 0.10.99 or later fileout << CLIENT_VERSION; // version that wrote the file minerPolicyEstimator->Write(fileout); } @@ -687,7 +357,7 @@ CTxMemPool::ReadFeeEstimates(CAutoFile& filein) return error("CTxMemPool::ReadFeeEstimates(): up-version (%d) fee estimate file", nVersionRequired); LOCK(cs); - minerPolicyEstimator->Read(filein, minRelayFee); + minerPolicyEstimator->Read(filein); } catch (const std::exception&) { LogPrintf("CTxMemPool::ReadFeeEstimates(): unable to read policy estimator data (non-fatal)"); @@ -724,6 +394,13 @@ void CTxMemPool::ClearPrioritisation(const uint256 hash) mapDeltas.erase(hash); } +bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const +{ + for (unsigned int i = 0; i < tx.vin.size(); i++) + if (exists(tx.vin[i].prevout.hash)) + return false; + return true; +} CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView *baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } diff --git a/src/txmempool.h b/src/txmempool.h index 0732af67e6..7271a5f603 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -43,10 +43,11 @@ private: int64_t nTime; //! Local time when entering the mempool double dPriority; //! Priority when entering the mempool unsigned int nHeight; //! Chain height when entering the mempool + bool hadNoDependencies; //! Not dependent on any other txs when it entered the mempool public: CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, - int64_t _nTime, double _dPriority, unsigned int _nHeight); + int64_t _nTime, double _dPriority, unsigned int _nHeight, bool poolHasNoInputsOf = false); CTxMemPoolEntry(); CTxMemPoolEntry(const CTxMemPoolEntry& other); @@ -56,9 +57,10 @@ public: size_t GetTxSize() const { return nTxSize; } int64_t GetTime() const { return nTime; } unsigned int GetHeight() const { return nHeight; } + bool WasClearAtEntry() const { return hadNoDependencies; } }; -class CMinerPolicyEstimator; +class CBlockPolicyEstimator; /** An inpoint - a combination of a transaction and an index n into its vin */ class CInPoint @@ -88,9 +90,8 @@ class CTxMemPool private: bool fSanityCheck; //! Normally false, true if -checkmempool or -regtest unsigned int nTransactionsUpdated; - CMinerPolicyEstimator* minerPolicyEstimator; + CBlockPolicyEstimator* minerPolicyEstimator; - CFeeRate minRelayFee; //! Passed to constructor to avoid dependency on main uint64_t totalTxSize; //! sum of all mempool tx' byte sizes public: @@ -111,17 +112,22 @@ public: void check(const CCoinsViewCache *pcoins) const; void setSanityCheck(bool _fSanityCheck) { fSanityCheck = _fSanityCheck; } - bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry); + bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate = true); void remove(const CTransaction &tx, std::list<CTransaction>& removed, bool fRecursive = false); void removeCoinbaseSpends(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight); void removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed); void removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight, - std::list<CTransaction>& conflicts); + std::list<CTransaction>& conflicts, bool fCurrentEstimate = true); void clear(); void queryHashes(std::vector<uint256>& vtxid); void pruneSpent(const uint256& hash, CCoins &coins); unsigned int GetTransactionsUpdated() const; void AddTransactionsUpdated(unsigned int n); + /** + * Check that none of this transactions inputs are in the mempool, and thus + * the tx is not dependent on other mempool transactions to be included in a block. + */ + bool HasNoInputsOf(const CTransaction& tx) const; /** Affect CreateNewBlock prioritisation of transactions */ void PrioritiseTransaction(const uint256 hash, const std::string strHash, double dPriorityDelta, const CAmount& nFeeDelta); @@ -139,7 +145,7 @@ public: return totalTxSize; } - bool exists(uint256 hash) + bool exists(uint256 hash) const { LOCK(cs); return (mapTx.count(hash) != 0); diff --git a/src/univalue/univalue.cpp b/src/univalue/univalue.cpp index 4e445a542a..6920c44c96 100644 --- a/src/univalue/univalue.cpp +++ b/src/univalue/univalue.cpp @@ -4,12 +4,17 @@ #include <stdint.h> #include <ctype.h> +#include <iomanip> #include <sstream> +#include <stdexcept> // std::runtime_error + #include "univalue.h" +#include "utilstrencodings.h" // ParseXX + using namespace std; -static const UniValue nullValue; +const UniValue NullUniValue; void UniValue::clear() { @@ -78,9 +83,11 @@ bool UniValue::setFloat(double val) string s; ostringstream oss; - oss << val; + oss << std::setprecision(16) << val; - return setNumStr(oss.str()); + bool ret = setNumStr(oss.str()); + typ = VREAL; + return ret; } bool UniValue::setStr(const string& val_) @@ -175,11 +182,11 @@ bool UniValue::checkObject(const std::map<std::string,UniValue::VType>& t) const UniValue& UniValue::operator[](const std::string& key) const { if (typ != VOBJ) - return nullValue; + return NullUniValue; int index = findKey(key); if (index < 0) - return nullValue; + return NullUniValue; return values[index]; } @@ -187,9 +194,9 @@ const UniValue& UniValue::operator[](const std::string& key) const const UniValue& UniValue::operator[](unsigned int index) const { if (typ != VOBJ && typ != VARR) - return nullValue; + return NullUniValue; if (index >= values.size()) - return nullValue; + return NullUniValue; return values[index]; } @@ -203,9 +210,95 @@ const char *uvTypeName(UniValue::VType t) case UniValue::VARR: return "array"; case UniValue::VSTR: return "string"; case UniValue::VNUM: return "number"; + case UniValue::VREAL: return "number"; } // not reached return NULL; } +const UniValue& find_value( const UniValue& obj, const std::string& name) +{ + for (unsigned int i = 0; i < obj.keys.size(); i++) + { + if( obj.keys[i] == name ) + { + return obj.values[i]; + } + } + + return NullUniValue; +} + +std::vector<std::string> UniValue::getKeys() const +{ + if (typ != VOBJ) + throw std::runtime_error("JSON value is not an object as expected"); + return keys; +} + +std::vector<UniValue> UniValue::getValues() const +{ + if (typ != VOBJ && typ != VARR) + throw std::runtime_error("JSON value is not an object or array as expected"); + return values; +} + +bool UniValue::get_bool() const +{ + if (typ != VBOOL) + throw std::runtime_error("JSON value is not a boolean as expected"); + return getBool(); +} + +std::string UniValue::get_str() const +{ + if (typ != VSTR) + throw std::runtime_error("JSON value is not a string as expected"); + return getValStr(); +} + +int UniValue::get_int() const +{ + if (typ != VNUM) + throw std::runtime_error("JSON value is not an integer as expected"); + int32_t retval; + if (!ParseInt32(getValStr(), &retval)) + throw std::runtime_error("JSON integer out of range"); + return retval; +} + +int64_t UniValue::get_int64() const +{ + if (typ != VNUM) + throw std::runtime_error("JSON value is not an integer as expected"); + int64_t retval; + if (!ParseInt64(getValStr(), &retval)) + throw std::runtime_error("JSON integer out of range"); + return retval; +} + +double UniValue::get_real() const +{ + if (typ != VREAL && typ != VNUM) + throw std::runtime_error("JSON value is not a number as expected"); + double retval; + if (!ParseDouble(getValStr(), &retval)) + throw std::runtime_error("JSON double out of range"); + return retval; +} + +const UniValue& UniValue::get_obj() const +{ + if (typ != VOBJ) + throw std::runtime_error("JSON value is not an object as expected"); + return *this; +} + +const UniValue& UniValue::get_array() const +{ + if (typ != VARR) + throw std::runtime_error("JSON value is not an array as expected"); + return *this; +} + diff --git a/src/univalue/univalue.h b/src/univalue/univalue.h index 88d73b8c64..54239741e2 100644 --- a/src/univalue/univalue.h +++ b/src/univalue/univalue.h @@ -11,9 +11,12 @@ #include <map> #include <cassert> +#include <sstream> // .get_int64() +#include <utility> // std::pair + class UniValue { public: - enum VType { VNULL, VOBJ, VARR, VSTR, VNUM, VBOOL, }; + enum VType { VNULL, VOBJ, VARR, VSTR, VNUM, VREAL, VBOOL, }; UniValue() { typ = VNULL; } UniValue(UniValue::VType initialType, const std::string& initialStr = "") { @@ -26,6 +29,9 @@ public: UniValue(int64_t val_) { setInt(val_); } + UniValue(bool val_) { + setBool(val_); + } UniValue(int val_) { setInt(val_); } @@ -55,10 +61,10 @@ public: bool setObject(); enum VType getType() const { return typ; } - std::string getValStr() const { return val; } + const std::string& getValStr() const { return val; } bool empty() const { return (values.size() == 0); } - size_t count() const { return values.size(); } + size_t size() const { return values.size(); } bool getBool() const { return isTrue(); } bool checkObject(const std::map<std::string,UniValue::VType>& memberTypes); @@ -68,10 +74,11 @@ public: bool isNull() const { return (typ == VNULL); } bool isTrue() const { return (typ == VBOOL) && (val == "1"); } - bool isFalse() const { return (!isTrue()); } + bool isFalse() const { return (typ == VBOOL) && (val != "1"); } bool isBool() const { return (typ == VBOOL); } bool isStr() const { return (typ == VSTR); } bool isNum() const { return (typ == VNUM); } + bool isReal() const { return (typ == VREAL); } bool isArray() const { return (typ == VARR); } bool isObject() const { return (typ == VOBJ); } @@ -130,8 +137,91 @@ private: int findKey(const std::string& key) const; void writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const; void writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const; + +public: + // Strict type-specific getters, these throw std::runtime_error if the + // value is of unexpected type + std::vector<std::string> getKeys() const; + std::vector<UniValue> getValues() const; + bool get_bool() const; + std::string get_str() const; + int get_int() const; + int64_t get_int64() const; + double get_real() const; + const UniValue& get_obj() const; + const UniValue& get_array() const; + + enum VType type() const { return getType(); } + bool push_back(std::pair<std::string,UniValue> pear) { + return pushKV(pear.first, pear.second); + } + friend const UniValue& find_value( const UniValue& obj, const std::string& name); }; +// +// The following were added for compatibility with json_spirit. +// Most duplicate other methods, and should be removed. +// +static inline std::pair<std::string,UniValue> Pair(const char *cKey, const char *cVal) +{ + std::string key(cKey); + UniValue uVal(cVal); + return std::make_pair(key, uVal); +} + +static inline std::pair<std::string,UniValue> Pair(const char *cKey, std::string strVal) +{ + std::string key(cKey); + UniValue uVal(strVal); + return std::make_pair(key, uVal); +} + +static inline std::pair<std::string,UniValue> Pair(const char *cKey, uint64_t u64Val) +{ + std::string key(cKey); + UniValue uVal(u64Val); + return std::make_pair(key, uVal); +} + +static inline std::pair<std::string,UniValue> Pair(const char *cKey, int64_t i64Val) +{ + std::string key(cKey); + UniValue uVal(i64Val); + return std::make_pair(key, uVal); +} + +static inline std::pair<std::string,UniValue> Pair(const char *cKey, bool iVal) +{ + std::string key(cKey); + UniValue uVal(iVal); + return std::make_pair(key, uVal); +} + +static inline std::pair<std::string,UniValue> Pair(const char *cKey, int iVal) +{ + std::string key(cKey); + UniValue uVal(iVal); + return std::make_pair(key, uVal); +} + +static inline std::pair<std::string,UniValue> Pair(const char *cKey, double dVal) +{ + std::string key(cKey); + UniValue uVal(dVal); + return std::make_pair(key, uVal); +} + +static inline std::pair<std::string,UniValue> Pair(const char *cKey, const UniValue& uVal) +{ + std::string key(cKey); + return std::make_pair(key, uVal); +} + +static inline std::pair<std::string,UniValue> Pair(std::string key, const UniValue& uVal) +{ + return std::make_pair(key, uVal); +} + enum jtokentype { JTOK_ERR = -1, JTOK_NONE = 0, // eof @@ -152,4 +242,8 @@ extern enum jtokentype getJsonToken(std::string& tokenVal, unsigned int& consumed, const char *raw); extern const char *uvTypeName(UniValue::VType t); +extern const UniValue NullUniValue; + +const UniValue& find_value( const UniValue& obj, const std::string& name); + #endif // BITCOIN_UNIVALUE_UNIVALUE_H diff --git a/src/univalue/univalue_write.cpp b/src/univalue/univalue_write.cpp index 9a1d364c95..d360c253b0 100644 --- a/src/univalue/univalue_write.cpp +++ b/src/univalue/univalue_write.cpp @@ -3,6 +3,8 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <ctype.h> +#include <iomanip> +#include <sstream> #include <stdio.h> #include "univalue.h" #include "univalue_escapes.h" @@ -59,6 +61,13 @@ string UniValue::write(unsigned int prettyIndent, case VSTR: s += "\"" + json_escape(val) + "\""; break; + case VREAL: + { + std::stringstream ss; + ss << std::showpoint << std::fixed << std::setprecision(8) << get_real(); + s += ss.str(); + } + break; case VNUM: s += val; break; diff --git a/src/util.cpp b/src/util.cpp index da5b259aee..da5821e530 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -7,11 +7,6 @@ #include "config/bitcoin-config.h" #endif -#if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)) -#include <pthread.h> -#include <pthread_np.h> -#endif - #include "util.h" #include "chainparamsbase.h" @@ -23,6 +18,11 @@ #include <stdarg.h> +#if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)) +#include <pthread.h> +#include <pthread_np.h> +#endif + #ifndef WIN32 // for posix_fallocate #ifdef __linux__ @@ -208,6 +208,7 @@ bool LogAcceptCategory(const char* category) // if not debugging everything and not debugging specific category, LogPrint does nothing. if (setCategories.count(string("")) == 0 && + setCategories.count(string("1")) == 0 && setCategories.count(string(category)) == 0) return false; } @@ -701,7 +702,7 @@ boost::filesystem::path GetTempPath() { #endif } -void runCommand(std::string strCommand) +void runCommand(const std::string& strCommand) { int nErr = ::system(strCommand.c_str()); if (nErr) diff --git a/src/util.h b/src/util.h index 483d9d7858..6ec81698ea 100644 --- a/src/util.h +++ b/src/util.h @@ -126,7 +126,7 @@ boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true); #endif boost::filesystem::path GetTempPath(); void ShrinkDebugFile(); -void runCommand(std::string strCommand); +void runCommand(const std::string& strCommand); inline bool IsSwitchChar(char c) { @@ -203,43 +203,6 @@ void SetThreadPriority(int nPriority); void RenameThread(const char* name); /** - * Standard wrapper for do-something-forever thread functions. - * "Forever" really means until the thread is interrupted. - * Use it like: - * new boost::thread(boost::bind(&LoopForever<void (*)()>, "dumpaddr", &DumpAddresses, 900000)); - * or maybe: - * boost::function<void()> f = boost::bind(&FunctionWithArg, argument); - * threadGroup.create_thread(boost::bind(&LoopForever<boost::function<void()> >, "nothing", f, milliseconds)); - */ -template <typename Callable> void LoopForever(const char* name, Callable func, int64_t msecs) -{ - std::string s = strprintf("bitcoin-%s", name); - RenameThread(s.c_str()); - LogPrintf("%s thread start\n", name); - try - { - while (1) - { - MilliSleep(msecs); - func(); - } - } - catch (const boost::thread_interrupted&) - { - LogPrintf("%s thread stop\n", name); - throw; - } - catch (const std::exception& e) { - PrintExceptionContinue(&e, name); - throw; - } - catch (...) { - PrintExceptionContinue(NULL, name); - throw; - } -} - -/** * .. and a wrapper that just calls func once */ template <typename Callable> void TraceThread(const char* name, Callable func) diff --git a/src/utilmoneystr.cpp b/src/utilmoneystr.cpp index 2fbc048878..0f3203432f 100644 --- a/src/utilmoneystr.cpp +++ b/src/utilmoneystr.cpp @@ -11,7 +11,7 @@ using namespace std; -string FormatMoney(const CAmount& n, bool fPlus) +std::string FormatMoney(const CAmount& n) { // Note: not using straight sprintf here because we do NOT want // localized number formatting. @@ -29,8 +29,6 @@ string FormatMoney(const CAmount& n, bool fPlus) if (n < 0) str.insert((unsigned int)0, 1, '-'); - else if (fPlus && n > 0) - str.insert((unsigned int)0, 1, '+'); return str; } diff --git a/src/utilmoneystr.h b/src/utilmoneystr.h index cd9b04810d..99c3ba8306 100644 --- a/src/utilmoneystr.h +++ b/src/utilmoneystr.h @@ -14,7 +14,7 @@ #include "amount.h" -std::string FormatMoney(const CAmount& n, bool fPlus=false); +std::string FormatMoney(const CAmount& n); bool ParseMoney(const std::string& str, CAmount& nRet); bool ParseMoney(const char* pszIn, CAmount& nRet); diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index c15bddc6fb..7d1de7d6a8 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -416,12 +416,25 @@ string DecodeBase32(const string& str) return (vchRet.size() == 0) ? string() : string((const char*)&vchRet[0], vchRet.size()); } +static bool ParsePrechecks(const std::string& str) +{ + if (str.empty()) // No empty string allowed + return false; + if (str.size() >= 1 && (isspace(str[0]) || isspace(str[str.size()-1]))) // No padding allowed + return false; + if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed + return false; + return true; +} + bool ParseInt32(const std::string& str, int32_t *out) { + if (!ParsePrechecks(str)) + return false; char *endp = NULL; errno = 0; // strtol will not set errno if valid long int n = strtol(str.c_str(), &endp, 10); - if(out) *out = (int)n; + if(out) *out = (int32_t)n; // Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit // platforms the size of these types may be different. @@ -430,7 +443,35 @@ bool ParseInt32(const std::string& str, int32_t *out) n <= std::numeric_limits<int32_t>::max(); } -std::string FormatParagraph(const std::string in, size_t width, size_t indent) +bool ParseInt64(const std::string& str, int64_t *out) +{ + if (!ParsePrechecks(str)) + return false; + char *endp = NULL; + errno = 0; // strtoll will not set errno if valid + long long int n = strtoll(str.c_str(), &endp, 10); + if(out) *out = (int64_t)n; + // Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow + // we still have to check that the returned value is within the range of an *int64_t*. + return endp && *endp == 0 && !errno && + n >= std::numeric_limits<int64_t>::min() && + n <= std::numeric_limits<int64_t>::max(); +} + +bool ParseDouble(const std::string& str, double *out) +{ + if (!ParsePrechecks(str)) + return false; + if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed + return false; + char *endp = NULL; + errno = 0; // strtod will not set errno if valid + double n = strtod(str.c_str(), &endp); + if(out) *out = n; + return endp && *endp == 0 && !errno; +} + +std::string FormatParagraph(const std::string& in, size_t width, size_t indent) { std::stringstream out; size_t col = 0; diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index b0edd8b542..58329b51bb 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -49,6 +49,20 @@ int atoi(const std::string& str); */ bool ParseInt32(const std::string& str, int32_t *out); +/** + * Convert string to signed 64-bit integer with strict parse error feedback. + * @returns true if the entire string could be parsed as valid integer, + * false if not the entire string could be parsed or when overflow or underflow occurred. + */ +bool ParseInt64(const std::string& str, int64_t *out); + +/** + * Convert string to double with strict parse error feedback. + * @returns true if the entire string could be parsed as valid double, + * false if not the entire string could be parsed or when overflow or underflow occurred. + */ +bool ParseDouble(const std::string& str, double *out); + template<typename T> std::string HexStr(const T itbegin, const T itend, bool fSpaces=false) { @@ -74,11 +88,11 @@ inline std::string HexStr(const T& vch, bool fSpaces=false) return HexStr(vch.begin(), vch.end(), fSpaces); } -/** +/** * Format a paragraph of text to a fixed width, adding spaces for * indentation to any added line. */ -std::string FormatParagraph(const std::string in, size_t width=79, size_t indent=0); +std::string FormatParagraph(const std::string& in, size_t width = 79, size_t indent = 0); /** * Timing-attack-resistant comparison. diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index aa9aefb0de..0532da5f37 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -14,7 +14,6 @@ CMainSignals& GetMainSignals() void RegisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2)); - g_signals.EraseTransaction.connect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1)); g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); @@ -28,7 +27,6 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); - g_signals.EraseTransaction.disconnect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1)); g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2)); } @@ -38,7 +36,6 @@ void UnregisterAllValidationInterfaces() { g_signals.Inventory.disconnect_all_slots(); g_signals.SetBestChain.disconnect_all_slots(); g_signals.UpdatedTransaction.disconnect_all_slots(); - g_signals.EraseTransaction.disconnect_all_slots(); g_signals.SyncTransaction.disconnect_all_slots(); } diff --git a/src/validationinterface.h b/src/validationinterface.h index b4c93d72ca..a911d1efeb 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -28,13 +28,12 @@ void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL); class CValidationInterface { protected: - virtual void SyncTransaction(const CTransaction &tx, const CBlock *pblock) {}; - virtual void EraseFromWallet(const uint256 &hash) {}; - virtual void SetBestChain(const CBlockLocator &locator) {}; - virtual void UpdatedTransaction(const uint256 &hash) {}; - virtual void Inventory(const uint256 &hash) {}; - virtual void ResendWalletTransactions(int64_t nBestBlockTime) {}; - virtual void BlockChecked(const CBlock&, const CValidationState&) {}; + virtual void SyncTransaction(const CTransaction &tx, const CBlock *pblock) {} + virtual void SetBestChain(const CBlockLocator &locator) {} + virtual void UpdatedTransaction(const uint256 &hash) {} + virtual void Inventory(const uint256 &hash) {} + virtual void ResendWalletTransactions(int64_t nBestBlockTime) {} + virtual void BlockChecked(const CBlock&, const CValidationState&) {} friend void ::RegisterValidationInterface(CValidationInterface*); friend void ::UnregisterValidationInterface(CValidationInterface*); friend void ::UnregisterAllValidationInterfaces(); @@ -43,8 +42,6 @@ protected: struct CMainSignals { /** Notifies listeners of updated transaction data (transaction, and optionally the block it is found in. */ boost::signals2::signal<void (const CTransaction &, const CBlock *)> SyncTransaction; - /** Notifies listeners of an erased transaction (currently disabled, requires transaction replacement). */ - boost::signals2::signal<void (const uint256 &)> EraseTransaction; /** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */ boost::signals2::signal<void (const uint256 &)> UpdatedTransaction; /** Notifies listeners of a new active block chain. */ diff --git a/src/wallet/db.h b/src/wallet/db.h index 9596723b21..2df6f6e5a9 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -20,9 +20,6 @@ #include <db_cxx.h> -class CDiskBlockIndex; -class COutPoint; - extern unsigned int nWalletDBUpdated; class CDBEnv diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index ab951d1d7d..b682a42e23 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -19,9 +19,8 @@ #include <boost/algorithm/string.hpp> #include <boost/date_time/posix_time/posix_time.hpp> -#include "json/json_spirit_value.h" +#include "univalue/univalue.h" -using namespace json_spirit; using namespace std; void EnsureWalletIsUnlocked(); @@ -70,10 +69,10 @@ std::string DecodeDumpString(const std::string &str) { return ret.str(); } -Value importprivkey(const Array& params, bool fHelp) +UniValue importprivkey(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() < 1 || params.size() > 3) throw runtime_error( @@ -126,7 +125,7 @@ Value importprivkey(const Array& params, bool fHelp) // Don't throw error in case a key is already there if (pwalletMain->HaveKey(vchAddress)) - return Value::null; + return NullUniValue; pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1; @@ -141,13 +140,13 @@ Value importprivkey(const Array& params, bool fHelp) } } - return Value::null; + return NullUniValue; } -Value importaddress(const Array& params, bool fHelp) +UniValue importaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() < 1 || params.size() > 3) throw runtime_error( @@ -200,7 +199,7 @@ Value importaddress(const Array& params, bool fHelp) // Don't throw error in case an address is already there if (pwalletMain->HaveWatchOnly(script)) - return Value::null; + return NullUniValue; pwalletMain->MarkDirty(); @@ -214,13 +213,13 @@ Value importaddress(const Array& params, bool fHelp) } } - return Value::null; + return NullUniValue; } -Value importwallet(const Array& params, bool fHelp) +UniValue importwallet(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() != 1) throw runtime_error( @@ -318,13 +317,13 @@ Value importwallet(const Array& params, bool fHelp) if (!fGood) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet"); - return Value::null; + return NullUniValue; } -Value dumpprivkey(const Array& params, bool fHelp) +UniValue dumpprivkey(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() != 1) throw runtime_error( @@ -359,10 +358,10 @@ Value dumpprivkey(const Array& params, bool fHelp) } -Value dumpwallet(const Array& params, bool fHelp) +UniValue dumpwallet(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() != 1) throw runtime_error( @@ -421,5 +420,5 @@ Value dumpwallet(const Array& params, bool fHelp) file << "\n"; file << "# End of dump\n"; file.close(); - return Value::null; + return NullUniValue; } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index dd5240e3c0..d284fcf15c 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -21,11 +21,9 @@ #include <boost/assign/list_of.hpp> -#include "json/json_spirit_utils.h" -#include "json/json_spirit_value.h" +#include "univalue/univalue.h" using namespace std; -using namespace json_spirit; int64_t nWalletUnlockTime; static CCriticalSection cs_nWalletUnlockTime; @@ -55,7 +53,7 @@ void EnsureWalletIsUnlocked() throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); } -void WalletTxToJSON(const CWalletTx& wtx, Object& entry) +void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) { int confirms = wtx.GetDepthInMainChain(); entry.push_back(Pair("confirmations", confirms)); @@ -69,7 +67,7 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry) } uint256 hash = wtx.GetHash(); entry.push_back(Pair("txid", hash.GetHex())); - Array conflicts; + UniValue conflicts(UniValue::VARR); BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts()) conflicts.push_back(conflict.GetHex()); entry.push_back(Pair("walletconflicts", conflicts)); @@ -79,7 +77,7 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry) entry.push_back(Pair(item.first, item.second)); } -string AccountFromValue(const Value& value) +string AccountFromValue(const UniValue& value) { string strAccount = value.get_str(); if (strAccount == "*") @@ -87,10 +85,10 @@ string AccountFromValue(const Value& value) return strAccount; } -Value getnewaddress(const Array& params, bool fHelp) +UniValue getnewaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() > 1) throw runtime_error( @@ -166,10 +164,10 @@ CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) return CBitcoinAddress(account.vchPubKey.GetID()); } -Value getaccountaddress(const Array& params, bool fHelp) +UniValue getaccountaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() != 1) throw runtime_error( @@ -191,17 +189,17 @@ Value getaccountaddress(const Array& params, bool fHelp) // Parse the account first so we don't generate a key if there's an error string strAccount = AccountFromValue(params[0]); - Value ret; + UniValue ret(UniValue::VSTR); ret = GetAccountAddress(strAccount).ToString(); return ret; } -Value getrawchangeaddress(const Array& params, bool fHelp) +UniValue getrawchangeaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() > 1) throw runtime_error( @@ -233,10 +231,10 @@ Value getrawchangeaddress(const Array& params, bool fHelp) } -Value setaccount(const Array& params, bool fHelp) +UniValue setaccount(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( @@ -275,14 +273,14 @@ Value setaccount(const Array& params, bool fHelp) else throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address"); - return Value::null; + return NullUniValue; } -Value getaccount(const Array& params, bool fHelp) +UniValue getaccount(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() != 1) throw runtime_error( @@ -311,10 +309,10 @@ Value getaccount(const Array& params, bool fHelp) } -Value getaddressesbyaccount(const Array& params, bool fHelp) +UniValue getaddressesbyaccount(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() != 1) throw runtime_error( @@ -337,7 +335,7 @@ Value getaddressesbyaccount(const Array& params, bool fHelp) string strAccount = AccountFromValue(params[0]); // Find all addresses that have the given account - Array ret; + UniValue ret(UniValue::VARR); BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) { const CBitcoinAddress& address = item.first; @@ -379,10 +377,10 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr throw JSONRPCError(RPC_WALLET_ERROR, "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); } -Value sendtoaddress(const Array& params, bool fHelp) +UniValue sendtoaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() < 2 || params.size() > 5) throw runtime_error( @@ -416,12 +414,14 @@ Value sendtoaddress(const Array& params, bool fHelp) // Amount CAmount nAmount = AmountFromValue(params[1]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); // Wallet comments CWalletTx wtx; - if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty()) + if (params.size() > 2 && !params[2].isNull() && !params[2].get_str().empty()) wtx.mapValue["comment"] = params[2].get_str(); - if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) + if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty()) wtx.mapValue["to"] = params[3].get_str(); bool fSubtractFeeFromAmount = false; @@ -435,10 +435,10 @@ Value sendtoaddress(const Array& params, bool fHelp) return wtx.GetHash().GetHex(); } -Value listaddressgroupings(const Array& params, bool fHelp) +UniValue listaddressgroupings(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp) throw runtime_error( @@ -465,14 +465,14 @@ Value listaddressgroupings(const Array& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - Array jsonGroupings; + UniValue jsonGroupings(UniValue::VARR); map<CTxDestination, CAmount> balances = pwalletMain->GetAddressBalances(); BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings()) { - Array jsonGrouping; + UniValue jsonGrouping(UniValue::VARR); BOOST_FOREACH(CTxDestination address, grouping) { - Array addressInfo; + UniValue addressInfo(UniValue::VARR); addressInfo.push_back(CBitcoinAddress(address).ToString()); addressInfo.push_back(ValueFromAmount(balances[address])); { @@ -487,10 +487,10 @@ Value listaddressgroupings(const Array& params, bool fHelp) return jsonGroupings; } -Value signmessage(const Array& params, bool fHelp) +UniValue signmessage(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() != 2) throw runtime_error( @@ -543,10 +543,10 @@ Value signmessage(const Array& params, bool fHelp) return EncodeBase64(&vchSig[0], vchSig.size()); } -Value getreceivedbyaddress(const Array& params, bool fHelp) +UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( @@ -588,7 +588,7 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !IsFinalTx(wtx)) + if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) continue; BOOST_FOREACH(const CTxOut& txout, wtx.vout) @@ -601,10 +601,10 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) } -Value getreceivedbyaccount(const Array& params, bool fHelp) +UniValue getreceivedbyaccount(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( @@ -642,7 +642,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !IsFinalTx(wtx)) + if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) continue; BOOST_FOREACH(const CTxOut& txout, wtx.vout) @@ -666,7 +666,7 @@ CAmount GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMi for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (!IsFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) + if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) continue; CAmount nReceived, nSent, nFee; @@ -690,10 +690,10 @@ CAmount GetAccountBalance(const string& strAccount, int nMinDepth, const isminef } -Value getbalance(const Array& params, bool fHelp) +UniValue getbalance(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() > 3) throw runtime_error( @@ -765,10 +765,10 @@ Value getbalance(const Array& params, bool fHelp) return ValueFromAmount(nBalance); } -Value getunconfirmedbalance(const Array ¶ms, bool fHelp) +UniValue getunconfirmedbalance(const UniValue ¶ms, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() > 0) throw runtime_error( @@ -781,10 +781,10 @@ Value getunconfirmedbalance(const Array ¶ms, bool fHelp) } -Value movecmd(const Array& params, bool fHelp) +UniValue movecmd(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() < 3 || params.size() > 5) throw runtime_error( @@ -811,6 +811,8 @@ Value movecmd(const Array& params, bool fHelp) string strFrom = AccountFromValue(params[0]); string strTo = AccountFromValue(params[1]); CAmount nAmount = AmountFromValue(params[2]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); if (params.size() > 3) // unused parameter, used to be nMinDepth, keep type-checking it though (void)params[3].get_int(); @@ -851,10 +853,10 @@ Value movecmd(const Array& params, bool fHelp) } -Value sendfrom(const Array& params, bool fHelp) +UniValue sendfrom(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() < 3 || params.size() > 6) throw runtime_error( @@ -890,15 +892,17 @@ Value sendfrom(const Array& params, bool fHelp) if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); CAmount nAmount = AmountFromValue(params[2]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); int nMinDepth = 1; if (params.size() > 3) nMinDepth = params[3].get_int(); CWalletTx wtx; wtx.strFromAccount = strAccount; - if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty()) + if (params.size() > 4 && !params[4].isNull() && !params[4].get_str().empty()) wtx.mapValue["comment"] = params[4].get_str(); - if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) + if (params.size() > 5 && !params[5].isNull() && !params[5].get_str().empty()) wtx.mapValue["to"] = params[5].get_str(); EnsureWalletIsUnlocked(); @@ -914,10 +918,10 @@ Value sendfrom(const Array& params, bool fHelp) } -Value sendmany(const Array& params, bool fHelp) +UniValue sendmany(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() < 2 || params.size() > 5) throw runtime_error( @@ -958,17 +962,17 @@ Value sendmany(const Array& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); string strAccount = AccountFromValue(params[0]); - Object sendTo = params[1].get_obj(); + UniValue sendTo = params[1].get_obj(); int nMinDepth = 1; if (params.size() > 2) nMinDepth = params[2].get_int(); CWalletTx wtx; wtx.strFromAccount = strAccount; - if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) + if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty()) wtx.mapValue["comment"] = params[3].get_str(); - Array subtractFeeFromAmount; + UniValue subtractFeeFromAmount(UniValue::VARR); if (params.size() > 4) subtractFeeFromAmount = params[4].get_array(); @@ -976,24 +980,29 @@ Value sendmany(const Array& params, bool fHelp) vector<CRecipient> vecSend; CAmount totalAmount = 0; - BOOST_FOREACH(const Pair& s, sendTo) + vector<string> keys = sendTo.getKeys(); + BOOST_FOREACH(const string& name_, keys) { - CBitcoinAddress address(s.name_); + CBitcoinAddress address(name_); if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+name_); if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_); + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); setAddress.insert(address); CScript scriptPubKey = GetScriptForDestination(address.Get()); - CAmount nAmount = AmountFromValue(s.value_); + CAmount nAmount = AmountFromValue(sendTo[name_]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); totalAmount += nAmount; bool fSubtractFeeFromAmount = false; - BOOST_FOREACH(const Value& addr, subtractFeeFromAmount) - if (addr.get_str() == s.name_) + for (unsigned int idx = 0; idx < subtractFeeFromAmount.size(); idx++) { + const UniValue& addr = subtractFeeFromAmount[idx]; + if (addr.get_str() == name_) fSubtractFeeFromAmount = true; + } CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount}; vecSend.push_back(recipient); @@ -1021,12 +1030,12 @@ Value sendmany(const Array& params, bool fHelp) } // Defined in rpcmisc.cpp -extern CScript _createmultisig_redeemScript(const Array& params); +extern CScript _createmultisig_redeemScript(const UniValue& params); -Value addmultisigaddress(const Array& params, bool fHelp) +UniValue addmultisigaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() < 2 || params.size() > 3) { @@ -1086,7 +1095,7 @@ struct tallyitem } }; -Value ListReceived(const Array& params, bool fByAccounts) +UniValue ListReceived(const UniValue& params, bool fByAccounts) { // Minimum confirmations int nMinDepth = 1; @@ -1109,7 +1118,7 @@ Value ListReceived(const Array& params, bool fByAccounts) { const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !IsFinalTx(wtx)) + if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) continue; int nDepth = wtx.GetDepthInMainChain(); @@ -1136,7 +1145,7 @@ Value ListReceived(const Array& params, bool fByAccounts) } // Reply - Array ret; + UniValue ret(UniValue::VARR); map<string, tallyitem> mapAccountTally; BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) { @@ -1165,14 +1174,14 @@ Value ListReceived(const Array& params, bool fByAccounts) } else { - Object obj; + UniValue obj(UniValue::VOBJ); if(fIsWatchonly) obj.push_back(Pair("involvesWatchonly", true)); obj.push_back(Pair("address", address.ToString())); obj.push_back(Pair("account", strAccount)); obj.push_back(Pair("amount", ValueFromAmount(nAmount))); obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf))); - Array transactions; + UniValue transactions(UniValue::VARR); if (it != mapTally.end()) { BOOST_FOREACH(const uint256& item, (*it).second.txids) @@ -1191,7 +1200,7 @@ Value ListReceived(const Array& params, bool fByAccounts) { CAmount nAmount = (*it).second.nAmount; int nConf = (*it).second.nConf; - Object obj; + UniValue obj(UniValue::VOBJ); if((*it).second.fIsWatchonly) obj.push_back(Pair("involvesWatchonly", true)); obj.push_back(Pair("account", (*it).first)); @@ -1204,10 +1213,10 @@ Value ListReceived(const Array& params, bool fByAccounts) return ret; } -Value listreceivedbyaddress(const Array& params, bool fHelp) +UniValue listreceivedbyaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() > 3) throw runtime_error( @@ -1241,10 +1250,10 @@ Value listreceivedbyaddress(const Array& params, bool fHelp) return ListReceived(params, false); } -Value listreceivedbyaccount(const Array& params, bool fHelp) +UniValue listreceivedbyaccount(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() > 3) throw runtime_error( @@ -1277,14 +1286,14 @@ Value listreceivedbyaccount(const Array& params, bool fHelp) return ListReceived(params, true); } -static void MaybePushAddress(Object & entry, const CTxDestination &dest) +static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) { CBitcoinAddress addr; if (addr.Set(dest)) entry.push_back(Pair("address", addr.ToString())); } -void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter) +void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) { CAmount nFee; string strSentAccount; @@ -1301,7 +1310,7 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe { BOOST_FOREACH(const COutputEntry& s, listSent) { - Object entry; + UniValue entry(UniValue::VOBJ); if(involvesWatchonly || (::IsMine(*pwalletMain, s.destination) & ISMINE_WATCH_ONLY)) entry.push_back(Pair("involvesWatchonly", true)); entry.push_back(Pair("account", strSentAccount)); @@ -1326,7 +1335,7 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe account = pwalletMain->mapAddressBook[r.destination].name; if (fAllAccounts || (account == strAccount)) { - Object entry; + UniValue entry(UniValue::VOBJ); if(involvesWatchonly || (::IsMine(*pwalletMain, r.destination) & ISMINE_WATCH_ONLY)) entry.push_back(Pair("involvesWatchonly", true)); entry.push_back(Pair("account", account)); @@ -1354,13 +1363,13 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe } } -void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret) +void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, UniValue& ret) { bool fAllAccounts = (strAccount == string("*")); if (fAllAccounts || acentry.strAccount == strAccount) { - Object entry; + UniValue entry(UniValue::VOBJ); entry.push_back(Pair("account", acentry.strAccount)); entry.push_back(Pair("category", "move")); entry.push_back(Pair("time", acentry.nTime)); @@ -1371,10 +1380,10 @@ void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Ar } } -Value listtransactions(const Array& params, bool fHelp) +UniValue listtransactions(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() > 4) throw runtime_error( @@ -1449,7 +1458,7 @@ Value listtransactions(const Array& params, bool fHelp) if (nFrom < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from"); - Array ret; + UniValue ret(UniValue::VARR); std::list<CAccountingEntry> acentries; CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount); @@ -1472,23 +1481,30 @@ Value listtransactions(const Array& params, bool fHelp) nFrom = ret.size(); if ((nFrom + nCount) > (int)ret.size()) nCount = ret.size() - nFrom; - Array::iterator first = ret.begin(); + + vector<UniValue> arrTmp = ret.getValues(); + + vector<UniValue>::iterator first = arrTmp.begin(); std::advance(first, nFrom); - Array::iterator last = ret.begin(); + vector<UniValue>::iterator last = arrTmp.begin(); std::advance(last, nFrom+nCount); - if (last != ret.end()) ret.erase(last, ret.end()); - if (first != ret.begin()) ret.erase(ret.begin(), first); + if (last != arrTmp.end()) arrTmp.erase(last, arrTmp.end()); + if (first != arrTmp.begin()) arrTmp.erase(arrTmp.begin(), first); + + std::reverse(arrTmp.begin(), arrTmp.end()); // Return oldest to newest - std::reverse(ret.begin(), ret.end()); // Return oldest to newest + ret.clear(); + ret.setArray(); + ret.push_backV(arrTmp); return ret; } -Value listaccounts(const Array& params, bool fHelp) +UniValue listaccounts(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() > 2) throw runtime_error( @@ -1558,17 +1574,17 @@ Value listaccounts(const Array& params, bool fHelp) BOOST_FOREACH(const CAccountingEntry& entry, acentries) mapAccountBalances[entry.strAccount] += entry.nCreditDebit; - Object ret; + UniValue ret(UniValue::VOBJ); BOOST_FOREACH(const PAIRTYPE(string, CAmount)& accountBalance, mapAccountBalances) { ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); } return ret; } -Value listsinceblock(const Array& params, bool fHelp) +UniValue listsinceblock(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp) throw runtime_error( @@ -1636,7 +1652,7 @@ Value listsinceblock(const Array& params, bool fHelp) int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; - Array transactions; + UniValue transactions(UniValue::VARR); for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++) { @@ -1649,17 +1665,17 @@ Value listsinceblock(const Array& params, bool fHelp) CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms]; uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256(); - Object ret; + UniValue ret(UniValue::VOBJ); ret.push_back(Pair("transactions", transactions)); ret.push_back(Pair("lastblock", lastblock.GetHex())); return ret; } -Value gettransaction(const Array& params, bool fHelp) +UniValue gettransaction(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( @@ -1707,7 +1723,7 @@ Value gettransaction(const Array& params, bool fHelp) if(params[1].get_bool()) filter = filter | ISMINE_WATCH_ONLY; - Object entry; + UniValue entry(UniValue::VOBJ); if (!pwalletMain->mapWallet.count(hash)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); const CWalletTx& wtx = pwalletMain->mapWallet[hash]; @@ -1723,7 +1739,7 @@ Value gettransaction(const Array& params, bool fHelp) WalletTxToJSON(wtx, entry); - Array details; + UniValue details(UniValue::VARR); ListTransactions(wtx, "*", 0, false, details, filter); entry.push_back(Pair("details", details)); @@ -1734,10 +1750,10 @@ Value gettransaction(const Array& params, bool fHelp) } -Value backupwallet(const Array& params, bool fHelp) +UniValue backupwallet(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() != 1) throw runtime_error( @@ -1756,14 +1772,14 @@ Value backupwallet(const Array& params, bool fHelp) if (!BackupWallet(*pwalletMain, strDest)) throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); - return Value::null; + return NullUniValue; } -Value keypoolrefill(const Array& params, bool fHelp) +UniValue keypoolrefill(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() > 1) throw runtime_error( @@ -1793,7 +1809,7 @@ Value keypoolrefill(const Array& params, bool fHelp) if (pwalletMain->GetKeyPoolSize() < kpSize) throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool."); - return Value::null; + return NullUniValue; } @@ -1804,10 +1820,10 @@ static void LockWallet(CWallet* pWallet) pWallet->Lock(); } -Value walletpassphrase(const Array& params, bool fHelp) +UniValue walletpassphrase(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) throw runtime_error( @@ -1860,14 +1876,14 @@ Value walletpassphrase(const Array& params, bool fHelp) nWalletUnlockTime = GetTime() + nSleepTime; RPCRunLater("lockwallet", boost::bind(LockWallet, pwalletMain), nSleepTime); - return Value::null; + return NullUniValue; } -Value walletpassphrasechange(const Array& params, bool fHelp) +UniValue walletpassphrasechange(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) throw runtime_error( @@ -1906,14 +1922,14 @@ Value walletpassphrasechange(const Array& params, bool fHelp) if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); - return Value::null; + return NullUniValue; } -Value walletlock(const Array& params, bool fHelp) +UniValue walletlock(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0)) throw runtime_error( @@ -1945,14 +1961,14 @@ Value walletlock(const Array& params, bool fHelp) nWalletUnlockTime = 0; } - return Value::null; + return NullUniValue; } -Value encryptwallet(const Array& params, bool fHelp) +UniValue encryptwallet(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1)) throw runtime_error( @@ -2006,10 +2022,10 @@ Value encryptwallet(const Array& params, bool fHelp) return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; } -Value lockunspent(const Array& params, bool fHelp) +UniValue lockunspent(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( @@ -2050,9 +2066,9 @@ Value lockunspent(const Array& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); if (params.size() == 1) - RPCTypeCheck(params, boost::assign::list_of(bool_type)); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VBOOL)); else - RPCTypeCheck(params, boost::assign::list_of(bool_type)(array_type)); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VBOOL)(UniValue::VARR)); bool fUnlock = params[0].get_bool(); @@ -2062,14 +2078,14 @@ Value lockunspent(const Array& params, bool fHelp) return true; } - Array outputs = params[1].get_array(); - BOOST_FOREACH(Value& output, outputs) - { - if (output.type() != obj_type) + UniValue outputs = params[1].get_array(); + for (unsigned int idx = 0; idx < outputs.size(); idx++) { + const UniValue& output = outputs[idx]; + if (!output.isObject()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object"); - const Object& o = output.get_obj(); + const UniValue& o = output.get_obj(); - RPCTypeCheck(o, boost::assign::map_list_of("txid", str_type)("vout", int_type)); + RPCTypeCheckObj(o, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)); string txid = find_value(o, "txid").get_str(); if (!IsHex(txid)) @@ -2090,10 +2106,10 @@ Value lockunspent(const Array& params, bool fHelp) return true; } -Value listlockunspent(const Array& params, bool fHelp) +UniValue listlockunspent(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() > 0) throw runtime_error( @@ -2126,10 +2142,10 @@ Value listlockunspent(const Array& params, bool fHelp) vector<COutPoint> vOutpts; pwalletMain->ListLockedCoins(vOutpts); - Array ret; + UniValue ret(UniValue::VARR); BOOST_FOREACH(COutPoint &outpt, vOutpts) { - Object o; + UniValue o(UniValue::VOBJ); o.push_back(Pair("txid", outpt.hash.GetHex())); o.push_back(Pair("vout", (int)outpt.n)); @@ -2139,10 +2155,10 @@ Value listlockunspent(const Array& params, bool fHelp) return ret; } -Value settxfee(const Array& params, bool fHelp) +UniValue settxfee(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() < 1 || params.size() > 1) throw runtime_error( @@ -2160,18 +2176,16 @@ Value settxfee(const Array& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); // Amount - CAmount nAmount = 0; - if (params[0].get_real() != 0.0) - nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts + CAmount nAmount = AmountFromValue(params[0]); payTxFee = CFeeRate(nAmount, 1000); return true; } -Value getwalletinfo(const Array& params, bool fHelp) +UniValue getwalletinfo(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() != 0) throw runtime_error( @@ -2195,7 +2209,7 @@ Value getwalletinfo(const Array& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - Object obj; + UniValue obj(UniValue::VOBJ); obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); obj.push_back(Pair("unconfirmed_balance", ValueFromAmount(pwalletMain->GetUnconfirmedBalance()))); @@ -2208,10 +2222,10 @@ Value getwalletinfo(const Array& params, bool fHelp) return obj; } -Value resendwallettransactions(const Array& params, bool fHelp) +UniValue resendwallettransactions(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() != 0) throw runtime_error( @@ -2225,7 +2239,7 @@ Value resendwallettransactions(const Array& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); std::vector<uint256> txids = pwalletMain->ResendWalletTransactionsBefore(GetTime()); - Array result; + UniValue result(UniValue::VARR); BOOST_FOREACH(const uint256& txid, txids) { result.push_back(txid.ToString()); @@ -2233,10 +2247,10 @@ Value resendwallettransactions(const Array& params, bool fHelp) return result; } -Value listunspent(const Array& params, bool fHelp) +UniValue listunspent(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) - return Value::null; + return NullUniValue; if (fHelp || params.size() > 3) throw runtime_error( @@ -2274,7 +2288,7 @@ Value listunspent(const Array& params, bool fHelp) + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") ); - RPCTypeCheck(params, boost::assign::list_of(int_type)(int_type)(array_type)); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)(UniValue::VNUM)(UniValue::VARR)); int nMinDepth = 1; if (params.size() > 0) @@ -2286,8 +2300,9 @@ Value listunspent(const Array& params, bool fHelp) set<CBitcoinAddress> setAddress; if (params.size() > 2) { - Array inputs = params[2].get_array(); - BOOST_FOREACH(Value& input, inputs) { + UniValue inputs = params[2].get_array(); + for (unsigned int idx = 0; idx < inputs.size(); idx++) { + const UniValue& input = inputs[idx]; CBitcoinAddress address(input.get_str()); if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+input.get_str()); @@ -2297,7 +2312,7 @@ Value listunspent(const Array& params, bool fHelp) } } - Array results; + UniValue results(UniValue::VARR); vector<COutput> vecOutputs; assert(pwalletMain != NULL); LOCK2(cs_main, pwalletMain->cs_wallet); @@ -2317,7 +2332,7 @@ Value listunspent(const Array& params, bool fHelp) CAmount nValue = out.tx->vout[out.i].nValue; const CScript& pk = out.tx->vout[out.i].scriptPubKey; - Object entry; + UniValue entry(UniValue::VOBJ); entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); entry.push_back(Pair("vout", out.i)); CTxDestination address; @@ -2343,4 +2358,4 @@ Value listunspent(const Array& params, bool fHelp) } return results; -}
\ No newline at end of file +} diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 0afdcd0011..3f12d88e79 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -9,6 +9,7 @@ #include "checkpoints.h" #include "coincontrol.h" #include "consensus/consensus.h" +#include "consensus/validation.h" #include "main.h" #include "net.h" #include "script/script.h" @@ -30,7 +31,7 @@ using namespace std; */ CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; -unsigned int nTxConfirmTarget = 1; +unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET; bool bSpendZeroConfChange = true; bool fSendFreeTransactions = false; bool fPayAtLeastCustomFee = true; @@ -775,18 +776,6 @@ void CWallet::SyncTransaction(const CTransaction& tx, const CBlock* pblock) } } -void CWallet::EraseFromWallet(const uint256 &hash) -{ - if (!fFileBacked) - return; - { - LOCK(cs_wallet); - if (mapWallet.erase(hash)) - CWalletDB(strWalletFile).EraseTx(hash); - } - return; -} - isminetype CWallet::IsMine(const CTxIn &txin) const { @@ -1317,7 +1306,7 @@ CAmount CWalletTx::GetChange() const bool CWalletTx::IsTrusted() const { // Quick answer in most cases - if (!IsFinalTx(*this)) + if (!CheckFinalTx(*this)) return false; int nDepth = GetDepthInMainChain(); if (nDepth >= 1) @@ -1423,7 +1412,7 @@ CAmount CWallet::GetUnconfirmedBalance() const for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; - if (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) + if (!CheckFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) nTotal += pcoin->GetAvailableCredit(); } } @@ -1468,7 +1457,7 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; - if (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) + if (!CheckFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) nTotal += pcoin->GetAvailableWatchOnlyCredit(); } } @@ -1503,7 +1492,7 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const const uint256& wtxid = it->first; const CWalletTx* pcoin = &(*it).second; - if (!IsFinalTx(*pcoin)) + if (!CheckFinalTx(*pcoin)) continue; if (fOnlyConfirmed && !pcoin->IsTrusted()) @@ -2290,7 +2279,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() { CWalletTx *pcoin = &walletEntry.second; - if (!IsFinalTx(*pcoin) || !pcoin->IsTrusted()) + if (!CheckFinalTx(*pcoin) || !pcoin->IsTrusted()) continue; if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) @@ -2413,7 +2402,7 @@ set< set<CTxDestination> > CWallet::GetAddressGroupings() return ret; } -set<CTxDestination> CWallet::GetAccountAddresses(string strAccount) const +std::set<CTxDestination> CWallet::GetAccountAddresses(const std::string& strAccount) const { LOCK(cs_wallet); set<CTxDestination> result; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 1c900b0315..9f3f08d117 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -44,6 +44,8 @@ static const CAmount DEFAULT_TRANSACTION_FEE = 0; static const CAmount nHighTransactionFeeWarning = 0.01 * COIN; //! -maxtxfee default static const CAmount DEFAULT_TRANSACTION_MAXFEE = 0.1 * COIN; +//! -txconfirmtarget default +static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 2; //! -maxtxfee will warn if called with a higher fee than this amount (in satoshis) static const CAmount nHighTransactionMaxFeeWarning = 100 * nHighTransactionFeeWarning; //! Largest (in bytes) free transaction we're willing to create @@ -494,7 +496,7 @@ public: SetNull(); } - CWallet(std::string strWalletFileIn) + CWallet(const std::string& strWalletFileIn) { SetNull(); @@ -613,7 +615,6 @@ public: bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb); void SyncTransaction(const CTransaction& tx, const CBlock* pblock); bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate); - void EraseFromWallet(const uint256 &hash); int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); void ReacceptWalletTransactions(); void ResendWalletTransactions(int64_t nBestBlockTime); @@ -643,7 +644,7 @@ public: std::set< std::set<CTxDestination> > GetAddressGroupings(); std::map<CTxDestination, CAmount> GetAddressBalances(); - std::set<CTxDestination> GetAccountAddresses(std::string strAccount) const; + std::set<CTxDestination> GetAccountAddresses(const std::string& strAccount) const; isminetype IsMine(const CTxIn& txin) const; CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const; diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index de56a2d1af..f777926e72 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -6,6 +6,7 @@ #include "wallet/walletdb.h" #include "base58.h" +#include "consensus/validation.h" #include "main.h" #include "protocol.h" #include "serialize.h" @@ -915,7 +916,7 @@ bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKe } std::vector<CDBEnv::KeyValPair> salvagedData; - bool allOK = dbenv.Salvage(newFilename, true, salvagedData); + bool fSuccess = dbenv.Salvage(newFilename, true, salvagedData); if (salvagedData.empty()) { LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename); @@ -923,7 +924,6 @@ bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKe } LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size()); - bool fSuccess = allOK; boost::scoped_ptr<Db> pdbCopy(new Db(dbenv.dbenv, 0)); int ret = pdbCopy->open(NULL, // Txn pointer filename.c_str(), // Filename |